summaryrefslogtreecommitdiff
path: root/net/packet/af_packet.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/packet/af_packet.c')
-rw-r--r--net/packet/af_packet.c44
1 files changed, 44 insertions, 0 deletions
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index ea81ccf3c7d6..f4001763134d 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -1496,6 +1496,7 @@ static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev,
DEFINE_MUTEX(fanout_mutex);
EXPORT_SYMBOL_GPL(fanout_mutex);
static LIST_HEAD(fanout_list);
+static u16 fanout_next_id;
static void __fanout_link(struct sock *sk, struct packet_sock *po)
{
@@ -1629,6 +1630,36 @@ static void fanout_release_data(struct packet_fanout *f)
};
}
+static bool __fanout_id_is_free(struct sock *sk, u16 candidate_id)
+{
+ struct packet_fanout *f;
+
+ list_for_each_entry(f, &fanout_list, list) {
+ if (f->id == candidate_id &&
+ read_pnet(&f->net) == sock_net(sk)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool fanout_find_new_id(struct sock *sk, u16 *new_id)
+{
+ u16 id = fanout_next_id;
+
+ do {
+ if (__fanout_id_is_free(sk, id)) {
+ *new_id = id;
+ fanout_next_id = id + 1;
+ return true;
+ }
+
+ id++;
+ } while (id != fanout_next_id);
+
+ return false;
+}
+
static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
{
struct packet_rollover *rollover = NULL;
@@ -1676,6 +1707,19 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
po->rollover = rollover;
}
+ if (type_flags & PACKET_FANOUT_FLAG_UNIQUEID) {
+ if (id != 0) {
+ err = -EINVAL;
+ goto out;
+ }
+ if (!fanout_find_new_id(sk, &id)) {
+ err = -ENOMEM;
+ goto out;
+ }
+ /* ephemeral flag for the first socket in the group: drop it */
+ flags &= ~(PACKET_FANOUT_FLAG_UNIQUEID >> 8);
+ }
+
match = NULL;
list_for_each_entry(f, &fanout_list, list) {
if (f->id == id &&