@@ -30,6 +30,11 @@ struct nfqnl_msg_packet_timestamp {
__aligned_be64 usec;
};
+struct nfqnl_msg_vlan {
+ __be16 proto;
+ __u16 tci;
+} __attribute__ ((packed));
+
enum nfqnl_attr_type {
NFQA_UNSPEC,
NFQA_PACKET_HDR,
@@ -50,6 +55,8 @@ enum nfqnl_attr_type {
NFQA_UID, /* __u32 sk uid */
NFQA_GID, /* __u32 sk gid */
NFQA_SECCTX, /* security context string */
+ NFQA_VLAN, /* packet vlan info */
+ NFQA_L2HDR, /* full L2 header */
__NFQA_MAX
};
@@ -295,6 +295,59 @@ static u32 nfqnl_get_sk_secctx(struct sk_buff *skb, char **secdata)
return seclen;
}
+static u32 nfqnl_get_bridge_nla_len(struct nf_queue_entry *entry)
+{
+ u32 nlalen = 0;
+
+ struct sk_buff *entskb = entry->skb;
+
+ if (skb_vlan_tag_present(entskb))
+ nlalen += nla_total_size(sizeof(struct nfqnl_msg_vlan));
+
+ if (entry->state.in && entskb->dev &&
+ (entskb->network_header > entskb->mac_header)) {
+ nlalen += nla_total_size((entskb->network_header -
+ entskb->mac_header));
+ }
+
+ return nlalen;
+}
+
+static int nfqnl_put_bridge_nla(struct nf_queue_entry *entry,
+ struct sk_buff *skb)
+{
+ struct sk_buff *entskb = entry->skb;
+
+ if (skb_vlan_tag_present(entskb)) {
+ struct nfqnl_msg_vlan pvlan;
+
+ pvlan.tci = entskb->vlan_tci;
+ pvlan.proto = entskb->vlan_proto;
+ if (nla_put(skb, NFQA_VLAN, sizeof(pvlan), &pvlan))
+ goto nla_put_failure;
+ }
+ if (entry->state.in && entskb->dev &&
+ (entskb->mac_header < entskb->network_header)) {
+ int len = (int)(entskb->network_header - entskb->mac_header);
+ struct nlattr *nla;
+
+ if (skb_tailroom(skb) <
+ NLA_ALIGN((sizeof(struct nlattr) + len)))
+ goto nla_put_failure;
+
+ nla = (struct nlattr *)
+ skb_put(skb, NLA_ALIGN(sizeof(struct nlattr) + len));
+ nla->nla_type = NFQA_L2HDR;
+ nla->nla_len = nla_attr_size(len);
+ memcpy(nla_data(nla), skb_mac_header(entskb), len);
+ }
+
+ return 0;
+
+nla_put_failure:
+ return -1;
+}
+
static struct sk_buff *
nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
struct nf_queue_entry *entry,
@@ -334,6 +387,9 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
if (entskb->tstamp.tv64)
size += nla_total_size(sizeof(struct nfqnl_msg_packet_timestamp));
+ if (entry->state.pf == PF_BRIDGE)
+ size += nfqnl_get_bridge_nla_len(entry);
+
if (entry->state.hook <= NF_INET_FORWARD ||
(entry->state.hook == NF_INET_POST_ROUTING && entskb->sk == NULL))
csum_verify = !skb_csum_unnecessary(entskb);
@@ -499,6 +555,11 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
}
}
+ if (entry->state.pf == PF_BRIDGE) {
+ if (nfqnl_put_bridge_nla(entry, skb))
+ goto nla_put_failure;
+ }
+
if (entskb->tstamp.tv64) {
struct nfqnl_msg_packet_timestamp ts;
struct timespec64 kts = ktime_to_timespec64(skb->tstamp);
@@ -536,7 +597,6 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
nla = (struct nlattr *)skb_put(skb, sizeof(*nla));
nla->nla_type = NFQA_PAYLOAD;
nla->nla_len = nla_attr_size(data_len);
-
if (skb_zerocopy(skb, entskb, data_len, hlen))
goto nla_put_failure;
}
@@ -1027,6 +1087,46 @@ static struct nf_conn *nfqnl_ct_parse(struct nfnl_ct_hook *nfnl_ct,
return ct;
}
+static int nfqnl_push_l2hdr(unsigned char **payload, int *payload_len,
+ struct nf_queue_entry *entry,
+ const struct nlattr * const nfqa[])
+{
+ int mac_header_len = 0;
+
+ if (entry->skb->dev && nfqa[NFQA_L2HDR]) {
+ mac_header_len = nla_len(nfqa[NFQA_L2HDR]);
+ if (mac_header_len > 0) {
+ unsigned char *full_payload;
+ /* realloc provided payload to have room for the l2
+ * header
+ */
+ full_payload = (char *)
+ kmalloc(((*payload_len) + mac_header_len),
+ GFP_ATOMIC);
+ if (!full_payload)
+ goto err_push;
+
+ memcpy(full_payload, nla_data(nfqa[NFQA_L2HDR]),
+ mac_header_len);
+ memcpy(full_payload + mac_header_len, *payload,
+ *payload_len);
+ *payload = full_payload;
+ (*payload_len) += mac_header_len;
+ /* push back mac header */
+ if (entry->skb->network_header > entry->skb->mac_header)
+ skb_push(entry->skb,
+ (entry->skb->network_header -
+ entry->skb->mac_header));
+ else
+ mac_header_len = 0;
+ }
+ }
+ return mac_header_len;
+
+err_push:
+ return 0;
+}
+
static int nfqnl_recv_verdict(struct net *net, struct sock *ctnl,
struct sk_buff *skb,
const struct nlmsghdr *nlh,
@@ -1068,14 +1168,34 @@ static int nfqnl_recv_verdict(struct net *net, struct sock *ctnl,
ct = nfqnl_ct_parse(nfnl_ct, nlh, nfqa, entry, &ctinfo);
}
+ if (nfqa[NFQA_VLAN]) {
+ struct nfqnl_msg_vlan *pvlan = nla_data(nfqa[NFQA_VLAN]);
+
+ entry->skb->vlan_tci = pvlan->tci;
+ entry->skb->vlan_proto = pvlan->proto;
+ }
+
if (nfqa[NFQA_PAYLOAD]) {
- u16 payload_len = nla_len(nfqa[NFQA_PAYLOAD]);
- int diff = payload_len - entry->skb->len;
+ int payload_len = nla_len(nfqa[NFQA_PAYLOAD]);
+ unsigned char *payload = nla_data(nfqa[NFQA_PAYLOAD]);
+ int mac_header_len = 0;
+ int diff = 0;
+
+ if (entry->state.pf == PF_BRIDGE)
+ mac_header_len = nfqnl_push_l2hdr(&payload,
+ &payload_len,
+ entry, nfqa);
- if (nfqnl_mangle(nla_data(nfqa[NFQA_PAYLOAD]),
- payload_len, entry, diff) < 0)
+ diff = payload_len - entry->skb->len;
+
+ if (nfqnl_mangle(payload, payload_len, entry, diff) < 0)
verdict = NF_DROP;
+ if (payload != nla_data(nfqa[NFQA_PAYLOAD]))
+ kfree(payload);
+ if (mac_header_len > 0) /* pull mac header again */
+ skb_pull(entry->skb, mac_header_len);
+
if (ct && diff)
nfnl_ct->seq_adjust(entry->skb, ct, ctinfo, diff);
}