+static int gw_put_job(struct sk_buff *skb, struct gw_job *gwj)
+{
+ struct {
+ struct can_frame cf;
+ u8 modtype;
+ } __attribute__((packed)) mb;
+
+ struct rtcanmsg *rtcan;
+ struct nlmsghdr *nlh = nlmsg_put(skb, 0, 0, 0, sizeof(*rtcan), 0);
+ if (!nlh)
+ return -EMSGSIZE;
+
+ rtcan = nlmsg_data(nlh);
+ rtcan->can_family = AF_CAN;
+ rtcan->src_ifindex = gwj->src_dev->ifindex;
+ rtcan->dst_ifindex = gwj->dst_dev->ifindex;
+ rtcan->can_txflags = 0;
+
+ if (gwj->flags & CAN_TX_ECHO)
+ rtcan->can_txflags |= CAN_GW_TXFLAGS_ECHO;
+
+ if (gwj->flags & CAN_TX_SRC_TSTAMP)
+ rtcan->can_txflags |= CAN_GW_TXFLAGS_SRC_TSTAMP;
+
+ /* check non default settings of attributes */
+ if (gwj->handled_frames) {
+ if (nla_put_u32(skb, CGW_HANDLED, gwj->handled_frames) < 0)
+ goto cancel;
+ else
+ nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(u32));
+ }
+
+ if (gwj->dropped_frames) {
+ if (nla_put_u32(skb, CGW_DROPPED, gwj->dropped_frames) < 0)
+ goto cancel;
+ else
+ nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(u32));
+ }
+
+ if (gwj->ccgw.filter.can_id || gwj->ccgw.filter.can_mask) {
+ if (nla_put(skb, CGW_FILTER, sizeof(struct can_filter),
+ &gwj->ccgw.filter) < 0)
+ goto cancel;
+ else
+ nlh->nlmsg_len += NLA_HDRLEN +
+ NLA_ALIGN(sizeof(struct can_filter));
+ }
+
+ if (gwj->ccgw.modtype.and) {
+ memcpy(&mb.cf, &gwj->ccgw.modframe.and, sizeof(mb.cf));
+ mb.modtype = gwj->ccgw.modtype.and;
+ if (nla_put(skb, CGW_MOD_AND, sizeof(mb), &mb) < 0)
+ goto cancel;
+ else
+ nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(mb));
+ }
+
+ if (gwj->ccgw.modtype.or) {
+ memcpy(&mb.cf, &gwj->ccgw.modframe.or, sizeof(mb.cf));
+ mb.modtype = gwj->ccgw.modtype.or;
+ if (nla_put(skb, CGW_MOD_OR, sizeof(mb), &mb) < 0)
+ goto cancel;
+ else
+ nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(mb));
+ }
+
+ if (gwj->ccgw.modtype.xor) {
+ memcpy(&mb.cf, &gwj->ccgw.modframe.xor, sizeof(mb.cf));
+ mb.modtype = gwj->ccgw.modtype.xor;
+ if (nla_put(skb, CGW_MOD_XOR, sizeof(mb), &mb) < 0)
+ goto cancel;
+ else
+ nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(mb));
+ }
+
+ if (gwj->ccgw.modtype.set) {
+ memcpy(&mb.cf, &gwj->ccgw.modframe.set, sizeof(mb.cf));
+ mb.modtype = gwj->ccgw.modtype.set;
+ if (nla_put(skb, CGW_MOD_SET, sizeof(mb), &mb) < 0)
+ goto cancel;
+ else
+ nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(mb));
+ }
+
+ return skb->len;
+
+cancel:
+ nlmsg_cancel(skb, nlh);
+ return -EMSGSIZE;
+}
+
+/* Dump information about all CAN gateway jobs, in response to RTM_GETROUTE */