Commit d253eee2 authored by Oliver Hartkopp's avatar Oliver Hartkopp Committed by David S. Miller

can: Fix CAN_(EFF|RTR)_FLAG handling in can_filter

Due to a wrong safety check in af_can.c it was not possible to filter
for SFF frames with a specific CAN identifier without getting the
same selected CAN identifier from a received EFF frame also.

This fix has a minimum (but user visible) impact on the CAN filter
API and therefore the CAN version is set to a new date.

Indeed the 'old' API is still working as-is. But when now setting
CAN_(EFF|RTR)_FLAG in can_filter.can_mask you might get less traffic
than before - but still the stuff that you expected to get for your
defined filter ...

Thanks to Kurt Van Dijck for pointing at this issue and for the review.
Signed-off-by: default avatarOliver Hartkopp <oliver@hartkopp.net>
Acked-by: default avatarKurt Van Dijck <kurt.van.dijck@eia.be>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent bd7df219
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#define CAN_VERSION "20071116" #define CAN_VERSION "20081130"
/* increment this number each time you change some user-space interface */ /* increment this number each time you change some user-space interface */
#define CAN_ABI_VERSION "8" #define CAN_ABI_VERSION "8"
......
...@@ -319,23 +319,52 @@ static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev) ...@@ -319,23 +319,52 @@ static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
return n ? d : NULL; return n ? d : NULL;
} }
/**
* find_rcv_list - determine optimal filterlist inside device filter struct
* @can_id: pointer to CAN identifier of a given can_filter
* @mask: pointer to CAN mask of a given can_filter
* @d: pointer to the device filter struct
*
* Description:
* Returns the optimal filterlist to reduce the filter handling in the
* receive path. This function is called by service functions that need
* to register or unregister a can_filter in the filter lists.
*
* A filter matches in general, when
*
* <received_can_id> & mask == can_id & mask
*
* so every bit set in the mask (even CAN_EFF_FLAG, CAN_RTR_FLAG) describe
* relevant bits for the filter.
*
* The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
* filter for error frames (CAN_ERR_FLAG bit set in mask). For error frames
* there is a special filterlist and a special rx path filter handling.
*
* Return:
* Pointer to optimal filterlist for the given can_id/mask pair.
* Constistency checked mask.
* Reduced can_id to have a preprocessed filter compare value.
*/
static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask, static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask,
struct dev_rcv_lists *d) struct dev_rcv_lists *d)
{ {
canid_t inv = *can_id & CAN_INV_FILTER; /* save flag before masking */ canid_t inv = *can_id & CAN_INV_FILTER; /* save flag before masking */
/* filter error frames */ /* filter for error frames in extra filterlist */
if (*mask & CAN_ERR_FLAG) { if (*mask & CAN_ERR_FLAG) {
/* clear CAN_ERR_FLAG in list entry */ /* clear CAN_ERR_FLAG in filter entry */
*mask &= CAN_ERR_MASK; *mask &= CAN_ERR_MASK;
return &d->rx[RX_ERR]; return &d->rx[RX_ERR];
} }
/* ensure valid values in can_mask */ /* with cleared CAN_ERR_FLAG we have a simple mask/value filterpair */
if (*mask & CAN_EFF_FLAG)
*mask &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG); #define CAN_EFF_RTR_FLAGS (CAN_EFF_FLAG | CAN_RTR_FLAG)
else
*mask &= (CAN_SFF_MASK | CAN_RTR_FLAG); /* ensure valid values in can_mask for 'SFF only' frame filtering */
if ((*mask & CAN_EFF_FLAG) && !(*can_id & CAN_EFF_FLAG))
*mask &= (CAN_SFF_MASK | CAN_EFF_RTR_FLAGS);
/* reduce condition testing at receive time */ /* reduce condition testing at receive time */
*can_id &= *mask; *can_id &= *mask;
...@@ -348,16 +377,20 @@ static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask, ...@@ -348,16 +377,20 @@ static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask,
if (!(*mask)) if (!(*mask))
return &d->rx[RX_ALL]; return &d->rx[RX_ALL];
/* use extra filterset for the subscription of exactly *ONE* can_id */ /* extra filterlists for the subscription of a single non-RTR can_id */
if (((*mask & CAN_EFF_RTR_FLAGS) == CAN_EFF_RTR_FLAGS)
&& !(*can_id & CAN_RTR_FLAG)) {
if (*can_id & CAN_EFF_FLAG) { if (*can_id & CAN_EFF_FLAG) {
if (*mask == (CAN_EFF_MASK | CAN_EFF_FLAG)) { if (*mask == (CAN_EFF_MASK | CAN_EFF_RTR_FLAGS)) {
/* RFC: a use-case for hash-tables in the future? */ /* RFC: a future use-case for hash-tables? */
return &d->rx[RX_EFF]; return &d->rx[RX_EFF];
} }
} else { } else {
if (*mask == CAN_SFF_MASK) if (*mask == (CAN_SFF_MASK | CAN_EFF_RTR_FLAGS))
return &d->rx_sff[*can_id]; return &d->rx_sff[*can_id];
} }
}
/* default: filter via can_id/can_mask */ /* default: filter via can_id/can_mask */
return &d->rx[RX_FIL]; return &d->rx[RX_FIL];
......
...@@ -64,10 +64,11 @@ ...@@ -64,10 +64,11 @@
#define BCM_CAN_DLC_MASK 0x0F /* clean private flags in can_dlc by masking */ #define BCM_CAN_DLC_MASK 0x0F /* clean private flags in can_dlc by masking */
/* get best masking value for can_rx_register() for a given single can_id */ /* get best masking value for can_rx_register() for a given single can_id */
#define REGMASK(id) ((id & CAN_RTR_FLAG) | ((id & CAN_EFF_FLAG) ? \ #define REGMASK(id) ((id & CAN_EFF_FLAG) ? \
(CAN_EFF_MASK | CAN_EFF_FLAG) : CAN_SFF_MASK)) (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG) : \
(CAN_SFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG))
#define CAN_BCM_VERSION "20080415" #define CAN_BCM_VERSION CAN_VERSION
static __initdata const char banner[] = KERN_INFO static __initdata const char banner[] = KERN_INFO
"can: broadcast manager protocol (rev " CAN_BCM_VERSION ")\n"; "can: broadcast manager protocol (rev " CAN_BCM_VERSION ")\n";
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment