cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

address-claim.c (6360B)


      1// SPDX-License-Identifier: GPL-2.0
      2// Copyright (c) 2010-2011 EIA Electronics,
      3//                         Kurt Van Dijck <kurt.van.dijck@eia.be>
      4// Copyright (c) 2010-2011 EIA Electronics,
      5//                         Pieter Beyens <pieter.beyens@eia.be>
      6// Copyright (c) 2017-2019 Pengutronix,
      7//                         Marc Kleine-Budde <kernel@pengutronix.de>
      8// Copyright (c) 2017-2019 Pengutronix,
      9//                         Oleksij Rempel <kernel@pengutronix.de>
     10
     11/* J1939 Address Claiming.
     12 * Address Claiming in the kernel
     13 * - keeps track of the AC states of ECU's,
     14 * - resolves NAME<=>SA taking into account the AC states of ECU's.
     15 *
     16 * All Address Claim msgs (including host-originated msg) are processed
     17 * at the receive path (a sent msg is always received again via CAN echo).
     18 * As such, the processing of AC msgs is done in the order on which msgs
     19 * are sent on the bus.
     20 *
     21 * This module doesn't send msgs itself (e.g. replies on Address Claims),
     22 * this is the responsibility of a user space application or daemon.
     23 */
     24
     25#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     26
     27#include <linux/netdevice.h>
     28#include <linux/skbuff.h>
     29
     30#include "j1939-priv.h"
     31
     32static inline name_t j1939_skb_to_name(const struct sk_buff *skb)
     33{
     34	return le64_to_cpup((__le64 *)skb->data);
     35}
     36
     37static inline bool j1939_ac_msg_is_request(struct sk_buff *skb)
     38{
     39	struct j1939_sk_buff_cb *skcb = j1939_skb_to_cb(skb);
     40	int req_pgn;
     41
     42	if (skb->len < 3 || skcb->addr.pgn != J1939_PGN_REQUEST)
     43		return false;
     44
     45	req_pgn = skb->data[0] | (skb->data[1] << 8) | (skb->data[2] << 16);
     46
     47	return req_pgn == J1939_PGN_ADDRESS_CLAIMED;
     48}
     49
     50static int j1939_ac_verify_outgoing(struct j1939_priv *priv,
     51				    struct sk_buff *skb)
     52{
     53	struct j1939_sk_buff_cb *skcb = j1939_skb_to_cb(skb);
     54
     55	if (skb->len != 8) {
     56		netdev_notice(priv->ndev, "tx address claim with dlc %i\n",
     57			      skb->len);
     58		return -EPROTO;
     59	}
     60
     61	if (skcb->addr.src_name != j1939_skb_to_name(skb)) {
     62		netdev_notice(priv->ndev, "tx address claim with different name\n");
     63		return -EPROTO;
     64	}
     65
     66	if (skcb->addr.sa == J1939_NO_ADDR) {
     67		netdev_notice(priv->ndev, "tx address claim with broadcast sa\n");
     68		return -EPROTO;
     69	}
     70
     71	/* ac must always be a broadcast */
     72	if (skcb->addr.dst_name || skcb->addr.da != J1939_NO_ADDR) {
     73		netdev_notice(priv->ndev, "tx address claim with dest, not broadcast\n");
     74		return -EPROTO;
     75	}
     76	return 0;
     77}
     78
     79int j1939_ac_fixup(struct j1939_priv *priv, struct sk_buff *skb)
     80{
     81	struct j1939_sk_buff_cb *skcb = j1939_skb_to_cb(skb);
     82	int ret;
     83	u8 addr;
     84
     85	/* network mgmt: address claiming msgs */
     86	if (skcb->addr.pgn == J1939_PGN_ADDRESS_CLAIMED) {
     87		struct j1939_ecu *ecu;
     88
     89		ret = j1939_ac_verify_outgoing(priv, skb);
     90		/* return both when failure & when successful */
     91		if (ret < 0)
     92			return ret;
     93		ecu = j1939_ecu_get_by_name(priv, skcb->addr.src_name);
     94		if (!ecu)
     95			return -ENODEV;
     96
     97		if (ecu->addr != skcb->addr.sa)
     98			/* hold further traffic for ecu, remove from parent */
     99			j1939_ecu_unmap(ecu);
    100		j1939_ecu_put(ecu);
    101	} else if (skcb->addr.src_name) {
    102		/* assign source address */
    103		addr = j1939_name_to_addr(priv, skcb->addr.src_name);
    104		if (!j1939_address_is_unicast(addr) &&
    105		    !j1939_ac_msg_is_request(skb)) {
    106			netdev_notice(priv->ndev, "tx drop: invalid sa for name 0x%016llx\n",
    107				      skcb->addr.src_name);
    108			return -EADDRNOTAVAIL;
    109		}
    110		skcb->addr.sa = addr;
    111	}
    112
    113	/* assign destination address */
    114	if (skcb->addr.dst_name) {
    115		addr = j1939_name_to_addr(priv, skcb->addr.dst_name);
    116		if (!j1939_address_is_unicast(addr)) {
    117			netdev_notice(priv->ndev, "tx drop: invalid da for name 0x%016llx\n",
    118				      skcb->addr.dst_name);
    119			return -EADDRNOTAVAIL;
    120		}
    121		skcb->addr.da = addr;
    122	}
    123	return 0;
    124}
    125
    126static void j1939_ac_process(struct j1939_priv *priv, struct sk_buff *skb)
    127{
    128	struct j1939_sk_buff_cb *skcb = j1939_skb_to_cb(skb);
    129	struct j1939_ecu *ecu, *prev;
    130	name_t name;
    131
    132	if (skb->len != 8) {
    133		netdev_notice(priv->ndev, "rx address claim with wrong dlc %i\n",
    134			      skb->len);
    135		return;
    136	}
    137
    138	name = j1939_skb_to_name(skb);
    139	skcb->addr.src_name = name;
    140	if (!name) {
    141		netdev_notice(priv->ndev, "rx address claim without name\n");
    142		return;
    143	}
    144
    145	if (!j1939_address_is_valid(skcb->addr.sa)) {
    146		netdev_notice(priv->ndev, "rx address claim with broadcast sa\n");
    147		return;
    148	}
    149
    150	write_lock_bh(&priv->lock);
    151
    152	/* Few words on the ECU ref counting:
    153	 *
    154	 * First we get an ECU handle, either with
    155	 * j1939_ecu_get_by_name_locked() (increments the ref counter)
    156	 * or j1939_ecu_create_locked() (initializes an ECU object
    157	 * with a ref counter of 1).
    158	 *
    159	 * j1939_ecu_unmap_locked() will decrement the ref counter,
    160	 * but only if the ECU was mapped before. So "ecu" still
    161	 * belongs to us.
    162	 *
    163	 * j1939_ecu_timer_start() will increment the ref counter
    164	 * before it starts the timer, so we can put the ecu when
    165	 * leaving this function.
    166	 */
    167	ecu = j1939_ecu_get_by_name_locked(priv, name);
    168	if (!ecu && j1939_address_is_unicast(skcb->addr.sa))
    169		ecu = j1939_ecu_create_locked(priv, name);
    170
    171	if (IS_ERR_OR_NULL(ecu))
    172		goto out_unlock_bh;
    173
    174	/* cancel pending (previous) address claim */
    175	j1939_ecu_timer_cancel(ecu);
    176
    177	if (j1939_address_is_idle(skcb->addr.sa)) {
    178		j1939_ecu_unmap_locked(ecu);
    179		goto out_ecu_put;
    180	}
    181
    182	/* save new addr */
    183	if (ecu->addr != skcb->addr.sa)
    184		j1939_ecu_unmap_locked(ecu);
    185	ecu->addr = skcb->addr.sa;
    186
    187	prev = j1939_ecu_get_by_addr_locked(priv, skcb->addr.sa);
    188	if (prev) {
    189		if (ecu->name > prev->name) {
    190			j1939_ecu_unmap_locked(ecu);
    191			j1939_ecu_put(prev);
    192			goto out_ecu_put;
    193		} else {
    194			/* kick prev if less or equal */
    195			j1939_ecu_unmap_locked(prev);
    196			j1939_ecu_put(prev);
    197		}
    198	}
    199
    200	j1939_ecu_timer_start(ecu);
    201 out_ecu_put:
    202	j1939_ecu_put(ecu);
    203 out_unlock_bh:
    204	write_unlock_bh(&priv->lock);
    205}
    206
    207void j1939_ac_recv(struct j1939_priv *priv, struct sk_buff *skb)
    208{
    209	struct j1939_sk_buff_cb *skcb = j1939_skb_to_cb(skb);
    210	struct j1939_ecu *ecu;
    211
    212	/* network mgmt */
    213	if (skcb->addr.pgn == J1939_PGN_ADDRESS_CLAIMED) {
    214		j1939_ac_process(priv, skb);
    215	} else if (j1939_address_is_unicast(skcb->addr.sa)) {
    216		/* assign source name */
    217		ecu = j1939_ecu_get_by_addr(priv, skcb->addr.sa);
    218		if (ecu) {
    219			skcb->addr.src_name = ecu->name;
    220			j1939_ecu_put(ecu);
    221		}
    222	}
    223
    224	/* assign destination name */
    225	ecu = j1939_ecu_get_by_addr(priv, skcb->addr.da);
    226	if (ecu) {
    227		skcb->addr.dst_name = ecu->name;
    228		j1939_ecu_put(ecu);
    229	}
    230}