Skip to content

Commit 047b64d

Browse files
committed
net, neigh: Extend neigh->flags to 32 bit to allow for extensions
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2193175 Conflicts: - context conflict due to existing backport of 77a23b1 ("net: add net device refcount tracker to struct pneigh_entry") commit 2c611ad Author: Roopa Prabhu <roopa@nvidia.com> Date: Mon Oct 11 14:12:37 2021 +0200 net, neigh: Extend neigh->flags to 32 bit to allow for extensions Currently, all bits in struct ndmsg's ndm_flags are used up with the most recent addition of 435f2e7 ("net: bridge: add support for sticky fdb entries"). This makes it impossible to extend the neighboring subsystem with new NTF_* flags: struct ndmsg { __u8 ndm_family; __u8 ndm_pad1; __u16 ndm_pad2; __s32 ndm_ifindex; __u16 ndm_state; __u8 ndm_flags; __u8 ndm_type; }; There are ndm_pad{1,2} attributes which are not used. However, due to uncareful design, the kernel does not enforce them to be zero upon new neighbor entry addition, and given they've been around forever, it is not possible to reuse them today due to risk of breakage. One option to overcome this limitation is to add a new NDA_FLAGS_EXT attribute for extended flags. In struct neighbour, there is a 3 byte hole between protocol and ha_lock, which allows neigh->flags to be extended from 8 to 32 bits while still being on the same cacheline as before. This also allows for all future NTF_* flags being in neigh->flags rather than yet another flags field. Unknown flags in NDA_FLAGS_EXT will be rejected by the kernel. Co-developed-by: Daniel Borkmann <daniel@iogearbox.net> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Signed-off-by: Roopa Prabhu <roopa@nvidia.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Ivan Vecera <ivecera@redhat.com>
1 parent 52a0f49 commit 047b64d

File tree

3 files changed

+50
-20
lines changed

3 files changed

+50
-20
lines changed

include/net/neighbour.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,11 @@ struct neighbour {
145145
struct timer_list timer;
146146
unsigned long used;
147147
atomic_t probes;
148-
__u8 flags;
149-
__u8 nud_state;
150-
__u8 type;
151-
__u8 dead;
148+
u8 nud_state;
149+
u8 type;
150+
u8 dead;
152151
u8 protocol;
152+
u32 flags;
153153
seqlock_t ha_lock;
154154
unsigned char ha[ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))] __aligned(8);
155155
struct hh_cache hh;
@@ -175,7 +175,7 @@ struct pneigh_entry {
175175
possible_net_t net;
176176
struct net_device *dev;
177177
netdevice_tracker dev_tracker;
178-
u8 flags;
178+
u32 flags;
179179
u8 protocol;
180180
u8 key[];
181181
};
@@ -261,6 +261,10 @@ static inline void *neighbour_priv(const struct neighbour *n)
261261
#define NEIGH_UPDATE_F_ISROUTER 0x40000000
262262
#define NEIGH_UPDATE_F_ADMIN 0x80000000
263263

264+
/* In-kernel representation for NDA_FLAGS_EXT flags: */
265+
#define NTF_OLD_MASK 0xff
266+
#define NTF_EXT_SHIFT 8
267+
264268
extern const struct nla_policy nda_policy[];
265269

266270
static inline bool neigh_key_eq16(const struct neighbour *n, const void *pkey)

include/uapi/linux/neighbour.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ enum {
3131
NDA_PROTOCOL, /* Originator of entry */
3232
NDA_NH_ID,
3333
NDA_FDB_EXT_ATTRS,
34+
NDA_FLAGS_EXT,
3435
__NDA_MAX
3536
};
3637

net/core/neighbour.c

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ static bool neigh_update_ext_learned(struct neighbour *neigh, u32 flags,
159159
int *notify)
160160
{
161161
bool rc = false;
162-
u8 ndm_flags;
162+
u32 ndm_flags;
163163

164164
if (!(flags & NEIGH_UPDATE_F_ADMIN))
165165
return rc;
@@ -379,7 +379,7 @@ EXPORT_SYMBOL(neigh_ifdown);
379379

380380
static struct neighbour *neigh_alloc(struct neigh_table *tbl,
381381
struct net_device *dev,
382-
u8 flags, bool exempt_from_gc)
382+
u32 flags, bool exempt_from_gc)
383383
{
384384
struct neighbour *n = NULL;
385385
unsigned long now = jiffies;
@@ -578,7 +578,7 @@ EXPORT_SYMBOL(neigh_lookup_nodev);
578578

579579
static struct neighbour *
580580
___neigh_create(struct neigh_table *tbl, const void *pkey,
581-
struct net_device *dev, u8 flags,
581+
struct net_device *dev, u32 flags,
582582
bool exempt_from_gc, bool want_ref)
583583
{
584584
u32 hash_val, key_len = tbl->key_len;
@@ -1788,6 +1788,7 @@ const struct nla_policy nda_policy[NDA_MAX+1] = {
17881788
[NDA_MASTER] = { .type = NLA_U32 },
17891789
[NDA_PROTOCOL] = { .type = NLA_U8 },
17901790
[NDA_NH_ID] = { .type = NLA_U32 },
1791+
[NDA_FLAGS_EXT] = { .type = NLA_U32 },
17911792
[NDA_FDB_EXT_ATTRS] = { .type = NLA_NESTED },
17921793
};
17931794

@@ -1860,7 +1861,7 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
18601861
struct netlink_ext_ack *extack)
18611862
{
18621863
int flags = NEIGH_UPDATE_F_ADMIN | NEIGH_UPDATE_F_OVERRIDE |
1863-
NEIGH_UPDATE_F_OVERRIDE_ISROUTER;
1864+
NEIGH_UPDATE_F_OVERRIDE_ISROUTER;
18641865
struct net *net = sock_net(skb->sk);
18651866
struct ndmsg *ndm;
18661867
struct nlattr *tb[NDA_MAX+1];
@@ -1869,6 +1870,7 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
18691870
struct neighbour *neigh;
18701871
void *dst, *lladdr;
18711872
u8 protocol = 0;
1873+
u32 ndm_flags;
18721874
int err;
18731875

18741876
ASSERT_RTNL();
@@ -1884,6 +1886,16 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
18841886
}
18851887

18861888
ndm = nlmsg_data(nlh);
1889+
ndm_flags = ndm->ndm_flags;
1890+
if (tb[NDA_FLAGS_EXT]) {
1891+
u32 ext = nla_get_u32(tb[NDA_FLAGS_EXT]);
1892+
1893+
if (ext & ~0) {
1894+
NL_SET_ERR_MSG(extack, "Invalid extended flags");
1895+
goto out;
1896+
}
1897+
ndm_flags |= (ext << NTF_EXT_SHIFT);
1898+
}
18871899
if (ndm->ndm_ifindex) {
18881900
dev = __dev_get_by_index(net, ndm->ndm_ifindex);
18891901
if (dev == NULL) {
@@ -1911,14 +1923,13 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
19111923

19121924
if (tb[NDA_PROTOCOL])
19131925
protocol = nla_get_u8(tb[NDA_PROTOCOL]);
1914-
1915-
if (ndm->ndm_flags & NTF_PROXY) {
1926+
if (ndm_flags & NTF_PROXY) {
19161927
struct pneigh_entry *pn;
19171928

19181929
err = -ENOBUFS;
19191930
pn = pneigh_lookup(tbl, net, dst, dev, 1);
19201931
if (pn) {
1921-
pn->flags = ndm->ndm_flags;
1932+
pn->flags = ndm_flags;
19221933
if (protocol)
19231934
pn->protocol = protocol;
19241935
err = 0;
@@ -1946,9 +1957,9 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
19461957
}
19471958

19481959
exempt_from_gc = ndm->ndm_state & NUD_PERMANENT ||
1949-
ndm->ndm_flags & NTF_EXT_LEARNED;
1960+
ndm_flags & NTF_EXT_LEARNED;
19501961
neigh = ___neigh_create(tbl, dst, dev,
1951-
ndm->ndm_flags & NTF_EXT_LEARNED,
1962+
ndm_flags & NTF_EXT_LEARNED,
19521963
exempt_from_gc, true);
19531964
if (IS_ERR(neigh)) {
19541965
err = PTR_ERR(neigh);
@@ -1968,16 +1979,16 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
19681979

19691980
if (protocol)
19701981
neigh->protocol = protocol;
1971-
if (ndm->ndm_flags & NTF_EXT_LEARNED)
1982+
if (ndm_flags & NTF_EXT_LEARNED)
19721983
flags |= NEIGH_UPDATE_F_EXT_LEARNED;
1973-
if (ndm->ndm_flags & NTF_ROUTER)
1984+
if (ndm_flags & NTF_ROUTER)
19741985
flags |= NEIGH_UPDATE_F_ISROUTER;
1975-
if (ndm->ndm_flags & NTF_USE)
1986+
if (ndm_flags & NTF_USE)
19761987
flags |= NEIGH_UPDATE_F_USE;
19771988

19781989
err = __neigh_update(neigh, lladdr, ndm->ndm_state, flags,
19791990
NETLINK_CB(skb).portid, extack);
1980-
if (!err && ndm->ndm_flags & NTF_USE) {
1991+
if (!err && ndm_flags & NTF_USE) {
19811992
neigh_event_send(neigh, NULL);
19821993
err = 0;
19831994
}
@@ -2432,6 +2443,7 @@ static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
24322443
static int neigh_fill_info(struct sk_buff *skb, struct neighbour *neigh,
24332444
u32 pid, u32 seq, int type, unsigned int flags)
24342445
{
2446+
u32 neigh_flags, neigh_flags_ext;
24352447
unsigned long now = jiffies;
24362448
struct nda_cacheinfo ci;
24372449
struct nlmsghdr *nlh;
@@ -2441,11 +2453,14 @@ static int neigh_fill_info(struct sk_buff *skb, struct neighbour *neigh,
24412453
if (nlh == NULL)
24422454
return -EMSGSIZE;
24432455

2456+
neigh_flags_ext = neigh->flags >> NTF_EXT_SHIFT;
2457+
neigh_flags = neigh->flags & NTF_OLD_MASK;
2458+
24442459
ndm = nlmsg_data(nlh);
24452460
ndm->ndm_family = neigh->ops->family;
24462461
ndm->ndm_pad1 = 0;
24472462
ndm->ndm_pad2 = 0;
2448-
ndm->ndm_flags = neigh->flags;
2463+
ndm->ndm_flags = neigh_flags;
24492464
ndm->ndm_type = neigh->type;
24502465
ndm->ndm_ifindex = neigh->dev->ifindex;
24512466

@@ -2476,6 +2491,8 @@ static int neigh_fill_info(struct sk_buff *skb, struct neighbour *neigh,
24762491

24772492
if (neigh->protocol && nla_put_u8(skb, NDA_PROTOCOL, neigh->protocol))
24782493
goto nla_put_failure;
2494+
if (neigh_flags_ext && nla_put_u32(skb, NDA_FLAGS_EXT, neigh_flags_ext))
2495+
goto nla_put_failure;
24792496

24802497
nlmsg_end(skb, nlh);
24812498
return 0;
@@ -2489,18 +2506,22 @@ static int pneigh_fill_info(struct sk_buff *skb, struct pneigh_entry *pn,
24892506
u32 pid, u32 seq, int type, unsigned int flags,
24902507
struct neigh_table *tbl)
24912508
{
2509+
u32 neigh_flags, neigh_flags_ext;
24922510
struct nlmsghdr *nlh;
24932511
struct ndmsg *ndm;
24942512

24952513
nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags);
24962514
if (nlh == NULL)
24972515
return -EMSGSIZE;
24982516

2517+
neigh_flags_ext = pn->flags >> NTF_EXT_SHIFT;
2518+
neigh_flags = pn->flags & NTF_OLD_MASK;
2519+
24992520
ndm = nlmsg_data(nlh);
25002521
ndm->ndm_family = tbl->family;
25012522
ndm->ndm_pad1 = 0;
25022523
ndm->ndm_pad2 = 0;
2503-
ndm->ndm_flags = pn->flags | NTF_PROXY;
2524+
ndm->ndm_flags = neigh_flags | NTF_PROXY;
25042525
ndm->ndm_type = RTN_UNICAST;
25052526
ndm->ndm_ifindex = pn->dev ? pn->dev->ifindex : 0;
25062527
ndm->ndm_state = NUD_NONE;
@@ -2510,6 +2531,8 @@ static int pneigh_fill_info(struct sk_buff *skb, struct pneigh_entry *pn,
25102531

25112532
if (pn->protocol && nla_put_u8(skb, NDA_PROTOCOL, pn->protocol))
25122533
goto nla_put_failure;
2534+
if (neigh_flags_ext && nla_put_u32(skb, NDA_FLAGS_EXT, neigh_flags_ext))
2535+
goto nla_put_failure;
25132536

25142537
nlmsg_end(skb, nlh);
25152538
return 0;
@@ -2818,6 +2841,7 @@ static inline size_t neigh_nlmsg_size(void)
28182841
+ nla_total_size(MAX_ADDR_LEN) /* NDA_LLADDR */
28192842
+ nla_total_size(sizeof(struct nda_cacheinfo))
28202843
+ nla_total_size(4) /* NDA_PROBES */
2844+
+ nla_total_size(4) /* NDA_FLAGS_EXT */
28212845
+ nla_total_size(1); /* NDA_PROTOCOL */
28222846
}
28232847

@@ -2846,6 +2870,7 @@ static inline size_t pneigh_nlmsg_size(void)
28462870
{
28472871
return NLMSG_ALIGN(sizeof(struct ndmsg))
28482872
+ nla_total_size(MAX_ADDR_LEN) /* NDA_DST */
2873+
+ nla_total_size(4) /* NDA_FLAGS_EXT */
28492874
+ nla_total_size(1); /* NDA_PROTOCOL */
28502875
}
28512876

0 commit comments

Comments
 (0)