|
@@ -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 &&
|