Skip to content

Commit 670dc28

Browse files
jmberg-intellinvjw
authored andcommitted
netlink: advertise incomplete dumps
Consider the following situation: * a dump that would show 8 entries, four in the first round, and four in the second * between the first and second rounds, 6 entries are removed * now the second round will not show any entry, and even if there is a sequence/generation counter the application will not know To solve this problem, add a new flag NLM_F_DUMP_INTR to the netlink header that indicates the dump wasn't consistent, this flag can also be set on the MSG_DONE message that terminates the dump, and as such above situation can be detected. To achieve this, add a sequence counter to the netlink callback struct. Of course, netlink code still needs to use this new functionality. The correct way to do that is to always set cb->seq when a dumpit callback is invoked and call nl_dump_check_consistent() for each new message. The core code will also call this function for the final MSG_DONE message. To make it usable with generic netlink, a new function genlmsg_nlhdr() is needed to obtain the netlink header from the genetlink user header. Signed-off-by: Johannes Berg <[email protected]> Acked-by: David S. Miller <[email protected]> Signed-off-by: John W. Linville <[email protected]>
1 parent c1c3dae commit 670dc28

File tree

4 files changed

+60
-0
lines changed

4 files changed

+60
-0
lines changed

include/linux/netlink.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ struct nlmsghdr {
4848
#define NLM_F_MULTI 2 /* Multipart message, terminated by NLMSG_DONE */
4949
#define NLM_F_ACK 4 /* Reply with ack, with zero or error code */
5050
#define NLM_F_ECHO 8 /* Echo this request */
51+
#define NLM_F_DUMP_INTR 16 /* Dump was inconsistent due to sequence change */
5152

5253
/* Modifiers to GET request */
5354
#define NLM_F_ROOT 0x100 /* specify tree root */
@@ -221,6 +222,7 @@ struct netlink_callback {
221222
struct netlink_callback *cb);
222223
int (*done)(struct netlink_callback *cb);
223224
int family;
225+
unsigned int prev_seq, seq;
224226
long args[6];
225227
};
226228

include/net/genetlink.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,38 @@ static inline void *genlmsg_put(struct sk_buff *skb, u32 pid, u32 seq,
159159
return (char *) hdr + GENL_HDRLEN;
160160
}
161161

162+
/**
163+
* genlmsg_nlhdr - Obtain netlink header from user specified header
164+
* @user_hdr: user header as returned from genlmsg_put()
165+
* @family: generic netlink family
166+
*
167+
* Returns pointer to netlink header.
168+
*/
169+
static inline struct nlmsghdr *genlmsg_nlhdr(void *user_hdr,
170+
struct genl_family *family)
171+
{
172+
return (struct nlmsghdr *)((char *)user_hdr -
173+
family->hdrsize -
174+
GENL_HDRLEN -
175+
NLMSG_HDRLEN);
176+
}
177+
178+
/**
179+
* genl_dump_check_consistent - check if sequence is consistent and advertise if not
180+
* @cb: netlink callback structure that stores the sequence number
181+
* @user_hdr: user header as returned from genlmsg_put()
182+
* @family: generic netlink family
183+
*
184+
* Cf. nl_dump_check_consistent(), this just provides a wrapper to make it
185+
* simpler to use with generic netlink.
186+
*/
187+
static inline void genl_dump_check_consistent(struct netlink_callback *cb,
188+
void *user_hdr,
189+
struct genl_family *family)
190+
{
191+
nl_dump_check_consistent(cb, genlmsg_nlhdr(user_hdr, family));
192+
}
193+
162194
/**
163195
* genlmsg_put_reply - Add generic netlink header to a reply message
164196
* @skb: socket buffer holding the message

include/net/netlink.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,30 @@ static inline int nlmsg_unicast(struct sock *sk, struct sk_buff *skb, u32 pid)
638638
nlmsg_ok(pos, rem); \
639639
pos = nlmsg_next(pos, &(rem)))
640640

641+
/**
642+
* nl_dump_check_consistent - check if sequence is consistent and advertise if not
643+
* @cb: netlink callback structure that stores the sequence number
644+
* @nlh: netlink message header to write the flag to
645+
*
646+
* This function checks if the sequence (generation) number changed during dump
647+
* and if it did, advertises it in the netlink message header.
648+
*
649+
* The correct way to use it is to set cb->seq to the generation counter when
650+
* all locks for dumping have been acquired, and then call this function for
651+
* each message that is generated.
652+
*
653+
* Note that due to initialisation concerns, 0 is an invalid sequence number
654+
* and must not be used by code that uses this functionality.
655+
*/
656+
static inline void
657+
nl_dump_check_consistent(struct netlink_callback *cb,
658+
struct nlmsghdr *nlh)
659+
{
660+
if (cb->prev_seq && cb->seq != cb->prev_seq)
661+
nlh->nlmsg_flags |= NLM_F_DUMP_INTR;
662+
cb->prev_seq = cb->seq;
663+
}
664+
641665
/**************************************************************************
642666
* Netlink Attributes
643667
**************************************************************************/

net/netlink/af_netlink.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1693,6 +1693,8 @@ static int netlink_dump(struct sock *sk)
16931693
if (!nlh)
16941694
goto errout_skb;
16951695

1696+
nl_dump_check_consistent(cb, nlh);
1697+
16961698
memcpy(nlmsg_data(nlh), &len, sizeof(len));
16971699

16981700
if (sk_filter(sk, skb))

0 commit comments

Comments
 (0)