Skip to content

Commit be1db4f

Browse files
committed
batman-adv: make the Distributed ARP Table vlan aware
The same IP subnet can be used on different VLANs, therefore DAT has to differentiate whether the IP to resolve belongs to one or the other virtual LAN. To accomplish this task DAT has to deal with the VLAN tag and store it together with each ARP entry. Signed-off-by: Antonio Quartulli <antonio@open-mesh.com> Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
1 parent bbb877e commit be1db4f

File tree

2 files changed

+107
-48
lines changed

2 files changed

+107
-48
lines changed

net/batman-adv/distributed-arp-table.c

Lines changed: 105 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include <linux/if_ether.h>
2121
#include <linux/if_arp.h>
22+
#include <linux/if_vlan.h>
2223
#include <net/arp.h>
2324

2425
#include "main.h"
@@ -205,15 +206,11 @@ static __be32 batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size)
205206
*/
206207
static uint32_t batadv_hash_dat(const void *data, uint32_t size)
207208
{
208-
const unsigned char *key = data;
209209
uint32_t hash = 0;
210-
size_t i;
210+
const struct batadv_dat_entry *dat = data;
211211

212-
for (i = 0; i < 4; i++) {
213-
hash += key[i];
214-
hash += (hash << 10);
215-
hash ^= (hash >> 6);
216-
}
212+
hash = batadv_hash_bytes(hash, &dat->ip, sizeof(dat->ip));
213+
hash = batadv_hash_bytes(hash, &dat->vid, sizeof(dat->vid));
217214

218215
hash += (hash << 3);
219216
hash ^= (hash >> 11);
@@ -227,21 +224,26 @@ static uint32_t batadv_hash_dat(const void *data, uint32_t size)
227224
* table
228225
* @bat_priv: the bat priv with all the soft interface information
229226
* @ip: search key
227+
* @vid: VLAN identifier
230228
*
231229
* Returns the dat_entry if found, NULL otherwise.
232230
*/
233231
static struct batadv_dat_entry *
234-
batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip)
232+
batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip,
233+
unsigned short vid)
235234
{
236235
struct hlist_head *head;
237-
struct batadv_dat_entry *dat_entry, *dat_entry_tmp = NULL;
236+
struct batadv_dat_entry to_find, *dat_entry, *dat_entry_tmp = NULL;
238237
struct batadv_hashtable *hash = bat_priv->dat.hash;
239238
uint32_t index;
240239

241240
if (!hash)
242241
return NULL;
243242

244-
index = batadv_hash_dat(&ip, hash->size);
243+
to_find.ip = ip;
244+
to_find.vid = vid;
245+
246+
index = batadv_hash_dat(&to_find, hash->size);
245247
head = &hash->table[index];
246248

247249
rcu_read_lock();
@@ -265,22 +267,24 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip)
265267
* @bat_priv: the bat priv with all the soft interface information
266268
* @ip: ipv4 to add/edit
267269
* @mac_addr: mac address to assign to the given ipv4
270+
* @vid: VLAN identifier
268271
*/
269272
static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
270-
uint8_t *mac_addr)
273+
uint8_t *mac_addr, unsigned short vid)
271274
{
272275
struct batadv_dat_entry *dat_entry;
273276
int hash_added;
274277

275-
dat_entry = batadv_dat_entry_hash_find(bat_priv, ip);
278+
dat_entry = batadv_dat_entry_hash_find(bat_priv, ip, vid);
276279
/* if this entry is already known, just update it */
277280
if (dat_entry) {
278281
if (!batadv_compare_eth(dat_entry->mac_addr, mac_addr))
279282
memcpy(dat_entry->mac_addr, mac_addr, ETH_ALEN);
280283
dat_entry->last_update = jiffies;
281284
batadv_dbg(BATADV_DBG_DAT, bat_priv,
282-
"Entry updated: %pI4 %pM\n", &dat_entry->ip,
283-
dat_entry->mac_addr);
285+
"Entry updated: %pI4 %pM (vid: %d)\n",
286+
&dat_entry->ip, dat_entry->mac_addr,
287+
BATADV_PRINT_VID(vid));
284288
goto out;
285289
}
286290

@@ -289,12 +293,13 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
289293
goto out;
290294

291295
dat_entry->ip = ip;
296+
dat_entry->vid = vid;
292297
memcpy(dat_entry->mac_addr, mac_addr, ETH_ALEN);
293298
dat_entry->last_update = jiffies;
294299
atomic_set(&dat_entry->refcount, 2);
295300

296301
hash_added = batadv_hash_add(bat_priv->dat.hash, batadv_compare_dat,
297-
batadv_hash_dat, &dat_entry->ip,
302+
batadv_hash_dat, dat_entry,
298303
&dat_entry->hash_entry);
299304

300305
if (unlikely(hash_added != 0)) {
@@ -303,8 +308,8 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
303308
goto out;
304309
}
305310

306-
batadv_dbg(BATADV_DBG_DAT, bat_priv, "New entry added: %pI4 %pM\n",
307-
&dat_entry->ip, dat_entry->mac_addr);
311+
batadv_dbg(BATADV_DBG_DAT, bat_priv, "New entry added: %pI4 %pM (vid: %d)\n",
312+
&dat_entry->ip, dat_entry->mac_addr, BATADV_PRINT_VID(vid));
308313

309314
out:
310315
if (dat_entry)
@@ -756,8 +761,8 @@ int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset)
756761
goto out;
757762

758763
seq_printf(seq, "Distributed ARP Table (%s):\n", net_dev->name);
759-
seq_printf(seq, " %-7s %-13s %5s\n", "IPv4", "MAC",
760-
"last-seen");
764+
seq_printf(seq, " %-7s %-9s %4s %11s\n", "IPv4",
765+
"MAC", "VID", "last-seen");
761766

762767
for (i = 0; i < hash->size; i++) {
763768
head = &hash->table[i];
@@ -770,8 +775,9 @@ int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset)
770775
last_seen_msecs = last_seen_msecs % 60000;
771776
last_seen_secs = last_seen_msecs / 1000;
772777

773-
seq_printf(seq, " * %15pI4 %14pM %6i:%02i\n",
778+
seq_printf(seq, " * %15pI4 %14pM %4i %6i:%02i\n",
774779
&dat_entry->ip, dat_entry->mac_addr,
780+
BATADV_PRINT_VID(dat_entry->vid),
775781
last_seen_mins, last_seen_secs);
776782
}
777783
rcu_read_unlock();
@@ -857,6 +863,31 @@ static uint16_t batadv_arp_get_type(struct batadv_priv *bat_priv,
857863
return type;
858864
}
859865

866+
/**
867+
* batadv_dat_get_vid - extract the VLAN identifier from skb if any
868+
* @skb: the buffer containing the packet to extract the VID from
869+
* @hdr_size: the size of the batman-adv header encapsulating the packet
870+
*
871+
* If the packet embedded in the skb is vlan tagged this function returns the
872+
* VID with the BATADV_VLAN_HAS_TAG flag. Otherwise BATADV_NO_FLAGS is returned.
873+
*/
874+
static unsigned short batadv_dat_get_vid(struct sk_buff *skb, int *hdr_size)
875+
{
876+
unsigned short vid;
877+
878+
vid = batadv_get_vid(skb, *hdr_size);
879+
880+
/* ARP parsing functions jump forward of hdr_size + ETH_HLEN.
881+
* If the header contained in the packet is a VLAN one (which is longer)
882+
* hdr_size is updated so that the functions will still skip the
883+
* correct amount of bytes.
884+
*/
885+
if (vid & BATADV_VLAN_HAS_TAG)
886+
*hdr_size += VLAN_HLEN;
887+
888+
return vid;
889+
}
890+
860891
/**
861892
* batadv_dat_snoop_outgoing_arp_request - snoop the ARP request and try to
862893
* answer using DAT
@@ -876,26 +907,31 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
876907
bool ret = false;
877908
struct batadv_dat_entry *dat_entry = NULL;
878909
struct sk_buff *skb_new;
910+
int hdr_size = 0;
911+
unsigned short vid;
879912

880913
if (!atomic_read(&bat_priv->distributed_arp_table))
881914
goto out;
882915

883-
type = batadv_arp_get_type(bat_priv, skb, 0);
916+
vid = batadv_dat_get_vid(skb, &hdr_size);
917+
918+
type = batadv_arp_get_type(bat_priv, skb, hdr_size);
884919
/* If the node gets an ARP_REQUEST it has to send a DHT_GET unicast
885920
* message to the selected DHT candidates
886921
*/
887922
if (type != ARPOP_REQUEST)
888923
goto out;
889924

890-
batadv_dbg_arp(bat_priv, skb, type, 0, "Parsing outgoing ARP REQUEST");
925+
batadv_dbg_arp(bat_priv, skb, type, hdr_size,
926+
"Parsing outgoing ARP REQUEST");
891927

892-
ip_src = batadv_arp_ip_src(skb, 0);
893-
hw_src = batadv_arp_hw_src(skb, 0);
894-
ip_dst = batadv_arp_ip_dst(skb, 0);
928+
ip_src = batadv_arp_ip_src(skb, hdr_size);
929+
hw_src = batadv_arp_hw_src(skb, hdr_size);
930+
ip_dst = batadv_arp_ip_dst(skb, hdr_size);
895931

896-
batadv_dat_entry_add(bat_priv, ip_src, hw_src);
932+
batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
897933

898-
dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst);
934+
dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);
899935
if (dat_entry) {
900936
/* If the ARP request is destined for a local client the local
901937
* client will answer itself. DAT would only generate a
@@ -917,11 +953,15 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
917953
if (!skb_new)
918954
goto out;
919955

956+
if (vid & BATADV_VLAN_HAS_TAG)
957+
skb_new = vlan_insert_tag(skb_new, htons(ETH_P_8021Q),
958+
vid & VLAN_VID_MASK);
959+
920960
skb_reset_mac_header(skb_new);
921961
skb_new->protocol = eth_type_trans(skb_new,
922962
bat_priv->soft_iface);
923963
bat_priv->stats.rx_packets++;
924-
bat_priv->stats.rx_bytes += skb->len + ETH_HLEN;
964+
bat_priv->stats.rx_bytes += skb->len + ETH_HLEN + hdr_size;
925965
bat_priv->soft_iface->last_rx = jiffies;
926966

927967
netif_rx(skb_new);
@@ -956,11 +996,14 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
956996
struct sk_buff *skb_new;
957997
struct batadv_dat_entry *dat_entry = NULL;
958998
bool ret = false;
999+
unsigned short vid;
9591000
int err;
9601001

9611002
if (!atomic_read(&bat_priv->distributed_arp_table))
9621003
goto out;
9631004

1005+
vid = batadv_dat_get_vid(skb, &hdr_size);
1006+
9641007
type = batadv_arp_get_type(bat_priv, skb, hdr_size);
9651008
if (type != ARPOP_REQUEST)
9661009
goto out;
@@ -972,9 +1015,9 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
9721015
batadv_dbg_arp(bat_priv, skb, type, hdr_size,
9731016
"Parsing incoming ARP REQUEST");
9741017

975-
batadv_dat_entry_add(bat_priv, ip_src, hw_src);
1018+
batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
9761019

977-
dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst);
1020+
dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);
9781021
if (!dat_entry)
9791022
goto out;
9801023

@@ -985,17 +1028,20 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
9851028
if (!skb_new)
9861029
goto out;
9871030

1031+
if (vid & BATADV_VLAN_HAS_TAG)
1032+
skb_new = vlan_insert_tag(skb_new, htons(ETH_P_8021Q),
1033+
vid & VLAN_VID_MASK);
1034+
9881035
/* To preserve backwards compatibility, the node has choose the outgoing
9891036
* format based on the incoming request packet type. The assumption is
9901037
* that a node not using the 4addr packet format doesn't support it.
9911038
*/
9921039
if (hdr_size == sizeof(struct batadv_unicast_4addr_packet))
9931040
err = batadv_send_skb_unicast_4addr(bat_priv, skb_new,
9941041
BATADV_P_DAT_CACHE_REPLY,
995-
BATADV_NO_FLAGS);
1042+
vid);
9961043
else
997-
err = batadv_send_skb_unicast(bat_priv, skb_new,
998-
BATADV_NO_FLAGS);
1044+
err = batadv_send_skb_unicast(bat_priv, skb_new, vid);
9991045

10001046
if (!err) {
10011047
batadv_inc_counter(bat_priv, BATADV_CNT_DAT_CACHED_REPLY_TX);
@@ -1020,23 +1066,28 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv,
10201066
uint16_t type;
10211067
__be32 ip_src, ip_dst;
10221068
uint8_t *hw_src, *hw_dst;
1069+
int hdr_size = 0;
1070+
unsigned short vid;
10231071

10241072
if (!atomic_read(&bat_priv->distributed_arp_table))
10251073
return;
10261074

1027-
type = batadv_arp_get_type(bat_priv, skb, 0);
1075+
vid = batadv_dat_get_vid(skb, &hdr_size);
1076+
1077+
type = batadv_arp_get_type(bat_priv, skb, hdr_size);
10281078
if (type != ARPOP_REPLY)
10291079
return;
10301080

1031-
batadv_dbg_arp(bat_priv, skb, type, 0, "Parsing outgoing ARP REPLY");
1081+
batadv_dbg_arp(bat_priv, skb, type, hdr_size,
1082+
"Parsing outgoing ARP REPLY");
10321083

1033-
hw_src = batadv_arp_hw_src(skb, 0);
1034-
ip_src = batadv_arp_ip_src(skb, 0);
1035-
hw_dst = batadv_arp_hw_dst(skb, 0);
1036-
ip_dst = batadv_arp_ip_dst(skb, 0);
1084+
hw_src = batadv_arp_hw_src(skb, hdr_size);
1085+
ip_src = batadv_arp_ip_src(skb, hdr_size);
1086+
hw_dst = batadv_arp_hw_dst(skb, hdr_size);
1087+
ip_dst = batadv_arp_ip_dst(skb, hdr_size);
10371088

1038-
batadv_dat_entry_add(bat_priv, ip_src, hw_src);
1039-
batadv_dat_entry_add(bat_priv, ip_dst, hw_dst);
1089+
batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
1090+
batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid);
10401091

10411092
/* Send the ARP reply to the candidates for both the IP addresses that
10421093
* the node obtained from the ARP reply
@@ -1058,10 +1109,13 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
10581109
__be32 ip_src, ip_dst;
10591110
uint8_t *hw_src, *hw_dst;
10601111
bool ret = false;
1112+
unsigned short vid;
10611113

10621114
if (!atomic_read(&bat_priv->distributed_arp_table))
10631115
goto out;
10641116

1117+
vid = batadv_dat_get_vid(skb, &hdr_size);
1118+
10651119
type = batadv_arp_get_type(bat_priv, skb, hdr_size);
10661120
if (type != ARPOP_REPLY)
10671121
goto out;
@@ -1077,13 +1131,13 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
10771131
/* Update our internal cache with both the IP addresses the node got
10781132
* within the ARP reply
10791133
*/
1080-
batadv_dat_entry_add(bat_priv, ip_src, hw_src);
1081-
batadv_dat_entry_add(bat_priv, ip_dst, hw_dst);
1134+
batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
1135+
batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid);
10821136

10831137
/* if this REPLY is directed to a client of mine, let's deliver the
10841138
* packet to the interface
10851139
*/
1086-
ret = !batadv_is_my_client(bat_priv, hw_dst, BATADV_NO_FLAGS);
1140+
ret = !batadv_is_my_client(bat_priv, hw_dst, vid);
10871141
out:
10881142
if (ret)
10891143
kfree_skb(skb);
@@ -1106,7 +1160,8 @@ bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
11061160
__be32 ip_dst;
11071161
struct batadv_dat_entry *dat_entry = NULL;
11081162
bool ret = false;
1109-
const size_t bcast_len = sizeof(struct batadv_bcast_packet);
1163+
int hdr_size = sizeof(struct batadv_bcast_packet);
1164+
unsigned short vid;
11101165

11111166
if (!atomic_read(&bat_priv->distributed_arp_table))
11121167
goto out;
@@ -1117,12 +1172,14 @@ bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
11171172
if (forw_packet->num_packets)
11181173
goto out;
11191174

1120-
type = batadv_arp_get_type(bat_priv, forw_packet->skb, bcast_len);
1175+
vid = batadv_dat_get_vid(forw_packet->skb, &hdr_size);
1176+
1177+
type = batadv_arp_get_type(bat_priv, forw_packet->skb, hdr_size);
11211178
if (type != ARPOP_REQUEST)
11221179
goto out;
11231180

1124-
ip_dst = batadv_arp_ip_dst(forw_packet->skb, bcast_len);
1125-
dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst);
1181+
ip_dst = batadv_arp_ip_dst(forw_packet->skb, hdr_size);
1182+
dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);
11261183
/* check if the node already got this entry */
11271184
if (!dat_entry) {
11281185
batadv_dbg(BATADV_DBG_DAT, bat_priv,

net/batman-adv/types.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,7 @@ struct batadv_algo_ops {
933933
* is used to stored ARP entries needed for the global DAT cache
934934
* @ip: the IPv4 corresponding to this DAT/ARP entry
935935
* @mac_addr: the MAC address associated to the stored IPv4
936+
* @vid: the vlan ID associated to this entry
936937
* @last_update: time in jiffies when this entry was refreshed last time
937938
* @hash_entry: hlist node for batadv_priv_dat::hash
938939
* @refcount: number of contexts the object is used
@@ -941,6 +942,7 @@ struct batadv_algo_ops {
941942
struct batadv_dat_entry {
942943
__be32 ip;
943944
uint8_t mac_addr[ETH_ALEN];
945+
unsigned short vid;
944946
unsigned long last_update;
945947
struct hlist_node hash_entry;
946948
atomic_t refcount;

0 commit comments

Comments
 (0)