#define CANXL_XLF 0x80 /* mandatory CAN XL frame flag (must always be set!) */
 #define CANXL_SEC 0x01 /* Simple Extended Content (security/segmentation) */
 
+/* the 8-bit VCID is optionally placed in the canxl_frame.prio element */
+#define CANXL_VCID_OFFSET 16 /* bit offset of VCID in prio element */
+#define CANXL_VCID_VAL_MASK 0xFFUL /* VCID is an 8-bit value */
+#define CANXL_VCID_MASK (CANXL_VCID_VAL_MASK << CANXL_VCID_OFFSET)
+
 /**
  * struct canxl_frame - CAN with e'X'tended frame 'L'ength frame structure
- * @prio:  11 bit arbitration priority with zero'ed CAN_*_FLAG flags
+ * @prio:  11 bit arbitration priority with zero'ed CAN_*_FLAG flags / VCID
  * @flags: additional flags for CAN XL
  * @sdt:   SDU (service data unit) type
  * @len:   frame payload length in byte (CANXL_MIN_DLEN .. CANXL_MAX_DLEN)
  * @prio shares the same position as @can_id from struct can[fd]_frame.
  */
 struct canxl_frame {
-       canid_t prio;  /* 11 bit priority for arbitration (canid_t) */
+       canid_t prio;  /* 11 bit priority for arbitration / 8 bit VCID */
        __u8    flags; /* additional flags for CAN XL */
        __u8    sdt;   /* SDU (service data unit) type */
        __u16   len;   /* frame payload length in byte */
 
        CAN_RAW_FD_FRAMES,      /* allow CAN FD frames (default:off) */
        CAN_RAW_JOIN_FILTERS,   /* all filters must match to trigger */
        CAN_RAW_XL_FRAMES,      /* allow CAN XL frames (default:off) */
+       CAN_RAW_XL_VCID_OPTS,   /* CAN XL VCID configuration options */
 };
 
+/* configuration for CAN XL virtual CAN identifier (VCID) handling */
+struct can_raw_vcid_options {
+
+       __u8 flags;             /* flags for vcid (filter) behaviour */
+       __u8 tx_vcid;           /* VCID value set into canxl_frame.prio */
+       __u8 rx_vcid;           /* VCID value for VCID filter */
+       __u8 rx_vcid_mask;      /* VCID mask for VCID filter */
+
+};
+
+/* can_raw_vcid_options.flags for CAN XL virtual CAN identifier handling */
+#define CAN_RAW_XL_VCID_TX_SET         0x01
+#define CAN_RAW_XL_VCID_TX_PASS                0x02
+#define CAN_RAW_XL_VCID_RX_FILTER      0x04
+
 #endif /* !_UAPI_CAN_RAW_H */
 
        int recv_own_msgs;
        int fd_frames;
        int xl_frames;
+       struct can_raw_vcid_options raw_vcid_opts;
+       canid_t tx_vcid_shifted;
+       canid_t rx_vcid_shifted;
+       canid_t rx_vcid_mask_shifted;
        int join_filters;
        int count;                 /* number of active filters */
        struct can_filter dfilter; /* default/single filter */
                return;
 
        /* make sure to not pass oversized frames to the socket */
-       if ((!ro->fd_frames && can_is_canfd_skb(oskb)) ||
-           (!ro->xl_frames && can_is_canxl_skb(oskb)))
+       if (!ro->fd_frames && can_is_canfd_skb(oskb))
                return;
 
+       if (can_is_canxl_skb(oskb)) {
+               struct canxl_frame *cxl = (struct canxl_frame *)oskb->data;
+
+               /* make sure to not pass oversized frames to the socket */
+               if (!ro->xl_frames)
+                       return;
+
+               /* filter CAN XL VCID content */
+               if (ro->raw_vcid_opts.flags & CAN_RAW_XL_VCID_RX_FILTER) {
+                       /* apply VCID filter if user enabled the filter */
+                       if ((cxl->prio & ro->rx_vcid_mask_shifted) !=
+                           (ro->rx_vcid_shifted & ro->rx_vcid_mask_shifted))
+                               return;
+               } else {
+                       /* no filter => do not forward VCID tagged frames */
+                       if (cxl->prio & CANXL_VCID_MASK)
+                               return;
+               }
+       }
+
        /* eliminate multiple filter matches for the same skb */
        if (this_cpu_ptr(ro->uniq)->skb == oskb &&
            this_cpu_ptr(ro->uniq)->skbcnt == can_skb_prv(oskb)->skbcnt) {
                        ro->fd_frames = ro->xl_frames;
                break;
 
+       case CAN_RAW_XL_VCID_OPTS:
+               if (optlen != sizeof(ro->raw_vcid_opts))
+                       return -EINVAL;
+
+               if (copy_from_sockptr(&ro->raw_vcid_opts, optval, optlen))
+                       return -EFAULT;
+
+               /* prepare 32 bit values for handling in hot path */
+               ro->tx_vcid_shifted = ro->raw_vcid_opts.tx_vcid << CANXL_VCID_OFFSET;
+               ro->rx_vcid_shifted = ro->raw_vcid_opts.rx_vcid << CANXL_VCID_OFFSET;
+               ro->rx_vcid_mask_shifted = ro->raw_vcid_opts.rx_vcid_mask << CANXL_VCID_OFFSET;
+               break;
+
        case CAN_RAW_JOIN_FILTERS:
                if (optlen != sizeof(ro->join_filters))
                        return -EINVAL;
                val = &ro->xl_frames;
                break;
 
+       case CAN_RAW_XL_VCID_OPTS:
+               /* user space buffer to small for VCID opts? */
+               if (len < sizeof(ro->raw_vcid_opts)) {
+                       /* return -ERANGE and needed space in optlen */
+                       err = -ERANGE;
+                       if (put_user(sizeof(ro->raw_vcid_opts), optlen))
+                               err = -EFAULT;
+               } else {
+                       if (len > sizeof(ro->raw_vcid_opts))
+                               len = sizeof(ro->raw_vcid_opts);
+                       if (copy_to_user(optval, &ro->raw_vcid_opts, len))
+                               err = -EFAULT;
+               }
+               break;
+
        case CAN_RAW_JOIN_FILTERS:
                if (len > sizeof(int))
                        len = sizeof(int);
        return 0;
 }
 
-static bool raw_bad_txframe(struct raw_sock *ro, struct sk_buff *skb, int mtu)
+static void raw_put_canxl_vcid(struct raw_sock *ro, struct sk_buff *skb)
+{
+       struct canxl_frame *cxl = (struct canxl_frame *)skb->data;
+
+       /* sanitize non CAN XL bits */
+       cxl->prio &= (CANXL_PRIO_MASK | CANXL_VCID_MASK);
+
+       /* clear VCID in CAN XL frame if pass through is disabled */
+       if (!(ro->raw_vcid_opts.flags & CAN_RAW_XL_VCID_TX_PASS))
+               cxl->prio &= CANXL_PRIO_MASK;
+
+       /* set VCID in CAN XL frame if enabled */
+       if (ro->raw_vcid_opts.flags & CAN_RAW_XL_VCID_TX_SET) {
+               cxl->prio &= CANXL_PRIO_MASK;
+               cxl->prio |= ro->tx_vcid_shifted;
+       }
+}
+
+static unsigned int raw_check_txframe(struct raw_sock *ro, struct sk_buff *skb, int mtu)
 {
        /* Classical CAN -> no checks for flags and device capabilities */
        if (can_is_can_skb(skb))
-               return false;
+               return CAN_MTU;
 
        /* CAN FD -> needs to be enabled and a CAN FD or CAN XL device */
        if (ro->fd_frames && can_is_canfd_skb(skb) &&
            (mtu == CANFD_MTU || can_is_canxl_dev_mtu(mtu)))
-               return false;
+               return CANFD_MTU;
 
        /* CAN XL -> needs to be enabled and a CAN XL device */
        if (ro->xl_frames && can_is_canxl_skb(skb) &&
            can_is_canxl_dev_mtu(mtu))
-               return false;
+               return CANXL_MTU;
 
-       return true;
+       return 0;
 }
 
 static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
        struct sockcm_cookie sockc;
        struct sk_buff *skb;
        struct net_device *dev;
+       unsigned int txmtu;
        int ifindex;
        int err = -EINVAL;
 
                goto free_skb;
 
        err = -EINVAL;
-       if (raw_bad_txframe(ro, skb, dev->mtu))
+
+       /* check for valid CAN (CC/FD/XL) frame content */
+       txmtu = raw_check_txframe(ro, skb, dev->mtu);
+       if (!txmtu)
                goto free_skb;
 
+       /* only CANXL: clear/forward/set VCID value */
+       if (txmtu == CANXL_MTU)
+               raw_put_canxl_vcid(ro, skb);
+
        sockcm_init(&sockc, sk);
        if (msg->msg_controllen) {
                err = sock_cmsg_send(sk, msg, &sockc);