@@ -393,6 +393,166 @@ void batadv_interface_rx(struct net_device *soft_iface,
393
393
return ;
394
394
}
395
395
396
+ /**
397
+ * batadv_softif_vlan_free_ref - decrease the vlan object refcounter and
398
+ * possibly free it
399
+ * @softif_vlan: the vlan object to release
400
+ */
401
+ static void batadv_softif_vlan_free_ref (struct batadv_softif_vlan * softif_vlan )
402
+ {
403
+ if (atomic_dec_and_test (& softif_vlan -> refcount ))
404
+ kfree_rcu (softif_vlan , rcu );
405
+ }
406
+
407
+ /**
408
+ * batadv_softif_vlan_get - get the vlan object for a specific vid
409
+ * @bat_priv: the bat priv with all the soft interface information
410
+ * @vid: the identifier of the vlan object to retrieve
411
+ *
412
+ * Returns the private data of the vlan matching the vid passed as argument or
413
+ * NULL otherwise. The refcounter of the returned object is incremented by 1.
414
+ */
415
+ static struct batadv_softif_vlan *
416
+ batadv_softif_vlan_get (struct batadv_priv * bat_priv , unsigned short vid )
417
+ {
418
+ struct batadv_softif_vlan * vlan_tmp , * vlan = NULL ;
419
+
420
+ rcu_read_lock ();
421
+ hlist_for_each_entry_rcu (vlan_tmp , & bat_priv -> softif_vlan_list , list ) {
422
+ if (vlan_tmp -> vid != vid )
423
+ continue ;
424
+
425
+ if (!atomic_inc_not_zero (& vlan_tmp -> refcount ))
426
+ continue ;
427
+
428
+ vlan = vlan_tmp ;
429
+ break ;
430
+ }
431
+ rcu_read_unlock ();
432
+
433
+ return vlan ;
434
+ }
435
+
436
+ /**
437
+ * batadv_create_vlan - allocate the needed resources for a new vlan
438
+ * @bat_priv: the bat priv with all the soft interface information
439
+ * @vid: the VLAN identifier
440
+ *
441
+ * Returns 0 on success, a negative error otherwise.
442
+ */
443
+ int batadv_softif_create_vlan (struct batadv_priv * bat_priv , unsigned short vid )
444
+ {
445
+ struct batadv_softif_vlan * vlan ;
446
+
447
+ vlan = batadv_softif_vlan_get (bat_priv , vid );
448
+ if (vlan ) {
449
+ batadv_softif_vlan_free_ref (vlan );
450
+ return - EEXIST ;
451
+ }
452
+
453
+ vlan = kzalloc (sizeof (* vlan ), GFP_ATOMIC );
454
+ if (!vlan )
455
+ return - ENOMEM ;
456
+
457
+ vlan -> vid = vid ;
458
+ atomic_set (& vlan -> refcount , 1 );
459
+
460
+ /* add a new TT local entry. This one will be marked with the NOPURGE
461
+ * flag
462
+ */
463
+ batadv_tt_local_add (bat_priv -> soft_iface ,
464
+ bat_priv -> soft_iface -> dev_addr , vid ,
465
+ BATADV_NULL_IFINDEX );
466
+
467
+ spin_lock_bh (& bat_priv -> softif_vlan_list_lock );
468
+ hlist_add_head_rcu (& vlan -> list , & bat_priv -> softif_vlan_list );
469
+ spin_unlock_bh (& bat_priv -> softif_vlan_list_lock );
470
+
471
+ return 0 ;
472
+ }
473
+
474
+ /**
475
+ * batadv_softif_destroy_vlan - remove and destroy a softif_vlan object
476
+ * @bat_priv: the bat priv with all the soft interface information
477
+ * @vlan: the object to remove
478
+ */
479
+ static void batadv_softif_destroy_vlan (struct batadv_priv * bat_priv ,
480
+ struct batadv_softif_vlan * vlan )
481
+ {
482
+ spin_lock_bh (& bat_priv -> softif_vlan_list_lock );
483
+ hlist_del_rcu (& vlan -> list );
484
+ spin_unlock_bh (& bat_priv -> softif_vlan_list_lock );
485
+
486
+ /* explicitly remove the associated TT local entry because it is marked
487
+ * with the NOPURGE flag
488
+ */
489
+ batadv_tt_local_remove (bat_priv , bat_priv -> soft_iface -> dev_addr ,
490
+ vlan -> vid , "vlan interface destroyed" , false);
491
+
492
+ batadv_softif_vlan_free_ref (vlan );
493
+ }
494
+
495
+ /**
496
+ * batadv_interface_add_vid - ndo_add_vid API implementation
497
+ * @dev: the netdev of the mesh interface
498
+ * @vid: identifier of the new vlan
499
+ *
500
+ * Set up all the internal structures for handling the new vlan on top of the
501
+ * mesh interface
502
+ *
503
+ * Returns 0 on success or a negative error code in case of failure.
504
+ */
505
+ static int batadv_interface_add_vid (struct net_device * dev , __be16 proto ,
506
+ unsigned short vid )
507
+ {
508
+ struct batadv_priv * bat_priv = netdev_priv (dev );
509
+
510
+ /* only 802.1Q vlans are supported.
511
+ * batman-adv does not know how to handle other types
512
+ */
513
+ if (proto != htons (ETH_P_8021Q ))
514
+ return - EINVAL ;
515
+
516
+ vid |= BATADV_VLAN_HAS_TAG ;
517
+
518
+ return batadv_softif_create_vlan (bat_priv , vid );
519
+ }
520
+
521
+ /**
522
+ * batadv_interface_kill_vid - ndo_kill_vid API implementation
523
+ * @dev: the netdev of the mesh interface
524
+ * @vid: identifier of the deleted vlan
525
+ *
526
+ * Destroy all the internal structures used to handle the vlan identified by vid
527
+ * on top of the mesh interface
528
+ *
529
+ * Returns 0 on success, -EINVAL if the specified prototype is not ETH_P_8021Q
530
+ * or -ENOENT if the specified vlan id wasn't registered.
531
+ */
532
+ static int batadv_interface_kill_vid (struct net_device * dev , __be16 proto ,
533
+ unsigned short vid )
534
+ {
535
+ struct batadv_priv * bat_priv = netdev_priv (dev );
536
+ struct batadv_softif_vlan * vlan ;
537
+
538
+ /* only 802.1Q vlans are supported. batman-adv does not know how to
539
+ * handle other types
540
+ */
541
+ if (proto != htons (ETH_P_8021Q ))
542
+ return - EINVAL ;
543
+
544
+ vlan = batadv_softif_vlan_get (bat_priv , vid | BATADV_VLAN_HAS_TAG );
545
+ if (!vlan )
546
+ return - ENOENT ;
547
+
548
+ batadv_softif_destroy_vlan (bat_priv , vlan );
549
+
550
+ /* finally free the vlan object */
551
+ batadv_softif_vlan_free_ref (vlan );
552
+
553
+ return 0 ;
554
+ }
555
+
396
556
/* batman-adv network devices have devices nesting below it and are a special
397
557
* "super class" of normal network devices; split their locks off into a
398
558
* separate class since they always nest.
@@ -432,13 +592,21 @@ static void batadv_set_lockdep_class(struct net_device *dev)
432
592
*/
433
593
static void batadv_softif_destroy_finish (struct work_struct * work )
434
594
{
595
+ struct batadv_softif_vlan * vlan ;
435
596
struct batadv_priv * bat_priv ;
436
597
struct net_device * soft_iface ;
437
598
438
599
bat_priv = container_of (work , struct batadv_priv ,
439
600
cleanup_work );
440
601
soft_iface = bat_priv -> soft_iface ;
441
602
603
+ /* destroy the "untagged" VLAN */
604
+ vlan = batadv_softif_vlan_get (bat_priv , BATADV_NO_FLAGS );
605
+ if (vlan ) {
606
+ batadv_softif_destroy_vlan (bat_priv , vlan );
607
+ batadv_softif_vlan_free_ref (vlan );
608
+ }
609
+
442
610
batadv_sysfs_del_meshif (soft_iface );
443
611
444
612
rtnl_lock ();
@@ -594,6 +762,8 @@ static const struct net_device_ops batadv_netdev_ops = {
594
762
.ndo_open = batadv_interface_open ,
595
763
.ndo_stop = batadv_interface_release ,
596
764
.ndo_get_stats = batadv_interface_stats ,
765
+ .ndo_vlan_rx_add_vid = batadv_interface_add_vid ,
766
+ .ndo_vlan_rx_kill_vid = batadv_interface_kill_vid ,
597
767
.ndo_set_mac_address = batadv_interface_set_mac_addr ,
598
768
.ndo_change_mtu = batadv_interface_change_mtu ,
599
769
.ndo_set_rx_mode = batadv_interface_set_rx_mode ,
@@ -633,6 +803,7 @@ static void batadv_softif_init_early(struct net_device *dev)
633
803
634
804
dev -> netdev_ops = & batadv_netdev_ops ;
635
805
dev -> destructor = batadv_softif_free ;
806
+ dev -> features |= NETIF_F_HW_VLAN_CTAG_FILTER ;
636
807
dev -> tx_queue_len = 0 ;
637
808
638
809
/* can't call min_mtu, because the needed variables
0 commit comments