Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[SKFILTER]: Add SKF_ADF_NLATTR instruction

SKF_ADF_NLATTR searches for a netlink attribute, which avoids manually
parsing and walking attributes. It takes the offset at which to start
searching in the 'A' register and the attribute type in the 'X' register
and returns the offset in the 'A' register. When the attribute is not
found it returns zero.

A top-level attribute can be located using a filter like this
(example for nfnetlink, using struct nfgenmsg):

...
{
/* A = offset of first attribute */
.code = BPF_LD | BPF_IMM,
.k = sizeof(struct nlmsghdr) + sizeof(struct nfgenmsg)
},
{
/* X = CTA_PROTOINFO */
.code = BPF_LDX | BPF_IMM,
.k = CTA_PROTOINFO,
},
{
/* A = netlink attribute offset */
.code = BPF_LD | BPF_B | BPF_ABS,
.k = SKF_AD_OFF + SKF_AD_NLATTR
},
{
/* Exit if not found */
.code = BPF_JMP | BPF_JEQ | BPF_K,
.k = 0,
.jt = <error>
},
...

A nested attribute below the CTA_PROTOINFO attribute would then
be parsed like this:

...
{
/* A += sizeof(struct nlattr) */
.code = BPF_ALU | BPF_ADD | BPF_K,
.k = sizeof(struct nlattr),
},
{
/* X = CTA_PROTOINFO_TCP */
.code = BPF_LDX | BPF_IMM,
.k = CTA_PROTOINFO_TCP,
},
{
/* A = netlink attribute offset */
.code = BPF_LD | BPF_B | BPF_ABS,
.k = SKF_AD_OFF + SKF_AD_NLATTR
},
...

The data of an attribute can be loaded into 'A' like this:

...
{
/* X = A (attribute offset) */
.code = BPF_MISC | BPF_TAX,
},
{
/* A = skb->data[X + k] */
.code = BPF_LD | BPF_B | BPF_IND,
.k = sizeof(struct nlattr),
},
...

Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Patrick McHardy and committed by
David S. Miller
4738c1db 3cccd607

+19 -1
+2 -1
include/linux/filter.h
··· 121 121 #define SKF_AD_PROTOCOL 0 122 122 #define SKF_AD_PKTTYPE 4 123 123 #define SKF_AD_IFINDEX 8 124 - #define SKF_AD_MAX 12 124 + #define SKF_AD_NLATTR 12 125 + #define SKF_AD_MAX 16 125 126 #define SKF_NET_OFF (-0x100000) 126 127 #define SKF_LL_OFF (-0x200000) 127 128
+17
net/core/filter.c
··· 27 27 #include <linux/if_packet.h> 28 28 #include <net/ip.h> 29 29 #include <net/protocol.h> 30 + #include <net/netlink.h> 30 31 #include <linux/skbuff.h> 31 32 #include <net/sock.h> 32 33 #include <linux/errno.h> ··· 304 303 case SKF_AD_IFINDEX: 305 304 A = skb->dev->ifindex; 306 305 continue; 306 + case SKF_AD_NLATTR: { 307 + struct nlattr *nla; 308 + 309 + if (skb_is_nonlinear(skb)) 310 + return 0; 311 + if (A > skb->len - sizeof(struct nlattr)) 312 + return 0; 313 + 314 + nla = nla_find((struct nlattr *)&skb->data[A], 315 + skb->len - A, X); 316 + if (nla) 317 + A = (void *)nla - (void *)skb->data; 318 + else 319 + A = 0; 320 + continue; 321 + } 307 322 default: 308 323 return 0; 309 324 }