Skip to content

Commit 33c563e

Browse files
committed
netfilter: nft_payload: skbuff vlan metadata mangle support
Userspace assumes vlan header is present at a given offset, but vlan offload allows to store this in metadata fields of the skbuff. Hence mangling vlan results in a garbled packet. Handle this transparently by adding a parser to the kernel. If vlan metadata is present and payload offset is over 12 bytes (source and destination mac address fields), then subtract vlan header present in vlan metadata, otherwise mangle vlan metadata based on offset and length, extracting data from the source register. This is similar to: 8cfd23e ("netfilter: nft_payload: work around vlan header stripping") to deal with vlan payload mangling. Fixes: 7ec3f7b ("netfilter: nft_payload: add packet mangling support") Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent aff5c01 commit 33c563e

File tree

1 file changed

+65
-7
lines changed

1 file changed

+65
-7
lines changed

net/netfilter/nft_payload.c

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,12 @@ int nft_payload_inner_offset(const struct nft_pktinfo *pkt)
145145
return pkt->inneroff;
146146
}
147147

148-
static bool nft_payload_need_vlan_copy(const struct nft_payload *priv)
148+
static bool nft_payload_need_vlan_adjust(u32 offset, u32 len)
149149
{
150-
unsigned int len = priv->offset + priv->len;
150+
unsigned int boundary = offset + len;
151151

152152
/* data past ether src/dst requested, copy needed */
153-
if (len > offsetof(struct ethhdr, h_proto))
153+
if (boundary > offsetof(struct ethhdr, h_proto))
154154
return true;
155155

156156
return false;
@@ -174,7 +174,7 @@ void nft_payload_eval(const struct nft_expr *expr,
174174
goto err;
175175

176176
if (skb_vlan_tag_present(skb) &&
177-
nft_payload_need_vlan_copy(priv)) {
177+
nft_payload_need_vlan_adjust(priv->offset, priv->len)) {
178178
if (!nft_payload_copy_vlan(dest, skb,
179179
priv->offset, priv->len))
180180
goto err;
@@ -801,21 +801,79 @@ struct nft_payload_set {
801801
u8 csum_flags;
802802
};
803803

804+
/* This is not struct vlan_hdr. */
805+
struct nft_payload_vlan_hdr {
806+
__be16 h_vlan_proto;
807+
__be16 h_vlan_TCI;
808+
};
809+
810+
static bool
811+
nft_payload_set_vlan(const u32 *src, struct sk_buff *skb, u8 offset, u8 len,
812+
int *vlan_hlen)
813+
{
814+
struct nft_payload_vlan_hdr *vlanh;
815+
__be16 vlan_proto;
816+
u16 vlan_tci;
817+
818+
if (offset >= offsetof(struct vlan_ethhdr, h_vlan_encapsulated_proto)) {
819+
*vlan_hlen = VLAN_HLEN;
820+
return true;
821+
}
822+
823+
switch (offset) {
824+
case offsetof(struct vlan_ethhdr, h_vlan_proto):
825+
if (len == 2) {
826+
vlan_proto = nft_reg_load_be16(src);
827+
skb->vlan_proto = vlan_proto;
828+
} else if (len == 4) {
829+
vlanh = (struct nft_payload_vlan_hdr *)src;
830+
__vlan_hwaccel_put_tag(skb, vlanh->h_vlan_proto,
831+
ntohs(vlanh->h_vlan_TCI));
832+
} else {
833+
return false;
834+
}
835+
break;
836+
case offsetof(struct vlan_ethhdr, h_vlan_TCI):
837+
if (len != 2)
838+
return false;
839+
840+
vlan_tci = ntohs(nft_reg_load_be16(src));
841+
skb->vlan_tci = vlan_tci;
842+
break;
843+
default:
844+
return false;
845+
}
846+
847+
return true;
848+
}
849+
804850
static void nft_payload_set_eval(const struct nft_expr *expr,
805851
struct nft_regs *regs,
806852
const struct nft_pktinfo *pkt)
807853
{
808854
const struct nft_payload_set *priv = nft_expr_priv(expr);
809-
struct sk_buff *skb = pkt->skb;
810855
const u32 *src = &regs->data[priv->sreg];
811-
int offset, csum_offset;
856+
int offset, csum_offset, vlan_hlen = 0;
857+
struct sk_buff *skb = pkt->skb;
812858
__wsum fsum, tsum;
813859

814860
switch (priv->base) {
815861
case NFT_PAYLOAD_LL_HEADER:
816862
if (!skb_mac_header_was_set(skb))
817863
goto err;
818-
offset = skb_mac_header(skb) - skb->data;
864+
865+
if (skb_vlan_tag_present(skb) &&
866+
nft_payload_need_vlan_adjust(priv->offset, priv->len)) {
867+
if (!nft_payload_set_vlan(src, skb,
868+
priv->offset, priv->len,
869+
&vlan_hlen))
870+
goto err;
871+
872+
if (!vlan_hlen)
873+
return;
874+
}
875+
876+
offset = skb_mac_header(skb) - skb->data - vlan_hlen;
819877
break;
820878
case NFT_PAYLOAD_NETWORK_HEADER:
821879
offset = skb_network_offset(skb);

0 commit comments

Comments
 (0)