diff options
author | Thomas Graf <tgraf@suug.ch> | 2005-11-09 20:25:53 -0500 |
---|---|---|
committer | Thomas Graf <tgr@axs.localdomain> | 2005-11-09 20:26:40 -0500 |
commit | 82ace47a7256fd39d370a6442e0649f75961b831 (patch) | |
tree | 261fafd7cb6b8b7bf023fa26cba1636ae2ec2f79 /net | |
parent | a8f74b228826eef1cbe04a05647d61e896f5fd63 (diff) |
[NETLINK]: Generic netlink receive queue processor
Introduces netlink_run_queue() to handle the receive queue of
a netlink socket in a generic way. Processes as much as there
was in the queue upon entry and invokes a callback function
for each netlink message found. The callback function may
refuse a message by returning a negative error code but setting
the error pointer to 0 in which case netlink_run_queue() will
return with a qlen != 0.
Signed-off-by: Thomas Graf <tgraf@suug.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/netlink/af_netlink.c | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index f3fb7e575816..8c38ee6d255e 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c | |||
@@ -58,6 +58,7 @@ | |||
58 | 58 | ||
59 | #include <net/sock.h> | 59 | #include <net/sock.h> |
60 | #include <net/scm.h> | 60 | #include <net/scm.h> |
61 | #include <net/netlink.h> | ||
61 | 62 | ||
62 | #define Nprintk(a...) | 63 | #define Nprintk(a...) |
63 | #define NLGRPSZ(x) (ALIGN(x, sizeof(unsigned long) * 8) / 8) | 64 | #define NLGRPSZ(x) (ALIGN(x, sizeof(unsigned long) * 8) / 8) |
@@ -1411,6 +1412,94 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err) | |||
1411 | netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT); | 1412 | netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT); |
1412 | } | 1413 | } |
1413 | 1414 | ||
1415 | static int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *, | ||
1416 | struct nlmsghdr *, int *)) | ||
1417 | { | ||
1418 | unsigned int total_len; | ||
1419 | struct nlmsghdr *nlh; | ||
1420 | int err; | ||
1421 | |||
1422 | while (skb->len >= nlmsg_total_size(0)) { | ||
1423 | nlh = (struct nlmsghdr *) skb->data; | ||
1424 | |||
1425 | if (skb->len < nlh->nlmsg_len) | ||
1426 | return 0; | ||
1427 | |||
1428 | total_len = min(NLMSG_ALIGN(nlh->nlmsg_len), skb->len); | ||
1429 | |||
1430 | if (cb(skb, nlh, &err) < 0) { | ||
1431 | /* Not an error, but we have to interrupt processing | ||
1432 | * here. Note: that in this case we do not pull | ||
1433 | * message from skb, it will be processed later. | ||
1434 | */ | ||
1435 | if (err == 0) | ||
1436 | return -1; | ||
1437 | netlink_ack(skb, nlh, err); | ||
1438 | } else if (nlh->nlmsg_flags & NLM_F_ACK) | ||
1439 | netlink_ack(skb, nlh, 0); | ||
1440 | |||
1441 | skb_pull(skb, total_len); | ||
1442 | } | ||
1443 | |||
1444 | return 0; | ||
1445 | } | ||
1446 | |||
1447 | /** | ||
1448 | * nelink_run_queue - Process netlink receive queue. | ||
1449 | * @sk: Netlink socket containing the queue | ||
1450 | * @qlen: Place to store queue length upon entry | ||
1451 | * @cb: Callback function invoked for each netlink message found | ||
1452 | * | ||
1453 | * Processes as much as there was in the queue upon entry and invokes | ||
1454 | * a callback function for each netlink message found. The callback | ||
1455 | * function may refuse a message by returning a negative error code | ||
1456 | * but setting the error pointer to 0 in which case this function | ||
1457 | * returns with a qlen != 0. | ||
1458 | * | ||
1459 | * qlen must be initialized to 0 before the initial entry, afterwards | ||
1460 | * the function may be called repeatedly until qlen reaches 0. | ||
1461 | */ | ||
1462 | void netlink_run_queue(struct sock *sk, unsigned int *qlen, | ||
1463 | int (*cb)(struct sk_buff *, struct nlmsghdr *, int *)) | ||
1464 | { | ||
1465 | struct sk_buff *skb; | ||
1466 | |||
1467 | if (!*qlen || *qlen > skb_queue_len(&sk->sk_receive_queue)) | ||
1468 | *qlen = skb_queue_len(&sk->sk_receive_queue); | ||
1469 | |||
1470 | for (; *qlen; (*qlen)--) { | ||
1471 | skb = skb_dequeue(&sk->sk_receive_queue); | ||
1472 | if (netlink_rcv_skb(skb, cb)) { | ||
1473 | if (skb->len) | ||
1474 | skb_queue_head(&sk->sk_receive_queue, skb); | ||
1475 | else { | ||
1476 | kfree_skb(skb); | ||
1477 | (*qlen)--; | ||
1478 | } | ||
1479 | break; | ||
1480 | } | ||
1481 | |||
1482 | kfree_skb(skb); | ||
1483 | } | ||
1484 | } | ||
1485 | |||
1486 | /** | ||
1487 | * netlink_queue_skip - Skip netlink message while processing queue. | ||
1488 | * @nlh: Netlink message to be skipped | ||
1489 | * @skb: Socket buffer containing the netlink messages. | ||
1490 | * | ||
1491 | * Pulls the given netlink message off the socket buffer so the next | ||
1492 | * call to netlink_queue_run() will not reconsider the message. | ||
1493 | */ | ||
1494 | void netlink_queue_skip(struct nlmsghdr *nlh, struct sk_buff *skb) | ||
1495 | { | ||
1496 | int msglen = NLMSG_ALIGN(nlh->nlmsg_len); | ||
1497 | |||
1498 | if (msglen > skb->len) | ||
1499 | msglen = skb->len; | ||
1500 | |||
1501 | skb_pull(skb, msglen); | ||
1502 | } | ||
1414 | 1503 | ||
1415 | #ifdef CONFIG_PROC_FS | 1504 | #ifdef CONFIG_PROC_FS |
1416 | struct nl_seq_iter { | 1505 | struct nl_seq_iter { |
@@ -1659,6 +1748,8 @@ out: | |||
1659 | core_initcall(netlink_proto_init); | 1748 | core_initcall(netlink_proto_init); |
1660 | 1749 | ||
1661 | EXPORT_SYMBOL(netlink_ack); | 1750 | EXPORT_SYMBOL(netlink_ack); |
1751 | EXPORT_SYMBOL(netlink_run_queue); | ||
1752 | EXPORT_SYMBOL(netlink_queue_skip); | ||
1662 | EXPORT_SYMBOL(netlink_broadcast); | 1753 | EXPORT_SYMBOL(netlink_broadcast); |
1663 | EXPORT_SYMBOL(netlink_dump_start); | 1754 | EXPORT_SYMBOL(netlink_dump_start); |
1664 | EXPORT_SYMBOL(netlink_kernel_create); | 1755 | EXPORT_SYMBOL(netlink_kernel_create); |