Hello list,
quite some time ago I sent a first version of this patchset together with other changes. At that time we decided to split the features up and to postpone the Extended Isolation for later sending. Now here it is!
This feature is an extension of the already existing "AP isolation" which aims to generalise the latter.
The idea is based on considering a particular subset of non-mesh clients as "ISOLATED" and then apply the same policy that batman-adv already applies for WiFi clients.
To decide which client belongs to this subset batman-adv uses the skb->mark field which value can be altered by several components in the kernel (e.g. netfilter). When an skb hits the soft-interface (e.g. bat0) the skb->mark is compared to a preconfigured value and the source client is classified as "ISOLATED" only in case of match.
The pre-configured mark (and its mask) is a user choice and can be set through a new sysfs interface that is added within this patchset.
"ISOLATED" clients won't be able to talk to each other (batman-adv will drop any packet originated by an isolated client and directed to another isolated client) like it now happens for WiFi ones (when AP isolation is on).
Moreover broadcast packets sent by ISOLATED clients are marked on the receiving node with the same mark that the user configured through the sysfs. In this way netfilter (or any other program) can make decisions about these packets on the receiver side (e.g. a rule could be "broadcast packets created by ISOLATED clients cannot be forwarded over any port of the bridge X")
A draft of the documentation (with an example of how to use tc to mark/filter packets) is available here[1] and will be improved as soon as the feature is released.
Cheers,
[1] http://www.open-mesh.org/projects/batman-adv/wiki/Extended-isolation
p.s. I don't know if this can make a difference, but this patchset is based on master + all my other patches pending on the mailing list.
Antonio Quartulli (5): batman-adv: add isolation_mark sysfs attribute batman-adv: mark a local client as isolated when needed batman-adv: print the new BATADV_TT_CLIENT_ISOLA flag batman-adv: extend the ap_isolation mechanism batman-adv: set the isolation mark in the skb if needed
main.h | 2 ++ multicast.c | 3 ++- packet.h | 1 + soft-interface.c | 23 ++++++++++++++---- sysfs.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++ translation-table.c | 57 ++++++++++++++++++++++++++++++++++++++++---- translation-table.h | 4 +++- types.h | 2 ++ 8 files changed, 148 insertions(+), 12 deletions(-)
From: Antonio Quartulli antonio@open-mesh.com
This attribute can be used to set and read the value and the mask of the skb mark which will be used to classify the source non-mesh client as ISOLATED. In this way a client can be advertised as such and the mark can potentially be restored at the receiving node before delivering the skb.
This can be helpful for creating network wide netfilter policies.
This sysfs file expects a string of the shape "$mark/$mask". Where $mark has to be a 32-bit number in any base, while $mask must be a 32bit mask expressed in hex base. Only bits in $mark covered by the bitmask are really stored.
Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- soft-interface.c | 2 ++ sysfs.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ types.h | 2 ++ 3 files changed, 72 insertions(+)
diff --git a/soft-interface.c b/soft-interface.c index a3797c5..c945cea 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -692,6 +692,8 @@ static int batadv_softif_init_late(struct net_device *dev) #endif bat_priv->tt.last_changeset = NULL; bat_priv->tt.last_changeset_len = 0; + bat_priv->isolation_mark = 0; + bat_priv->isolation_mark_mask = 0;
/* randomize initial seqno to avoid collision */ get_random_bytes(&random_seqno, sizeof(random_seqno)); diff --git a/sysfs.c b/sysfs.c index 98f8568..af4d661 100644 --- a/sysfs.c +++ b/sysfs.c @@ -447,6 +447,71 @@ static ssize_t batadv_store_gw_bwidth(struct kobject *kobj, return batadv_gw_bandwidth_set(net_dev, buff, count); }
+/** + * batadv_store_isolation_mark - parse and store the isolation mark/mask entered by + * the user + * @kobj: kobject representing the private mesh sysfs directory + * @attr: the batman-adv attribute the user is interacting with + * @buf: the buffer containing the user data + * @count: number of bytes in the buffer + * + * Returns 'count' on success or a negative error code in case of failure + */ +static ssize_t batadv_store_isolation_mark(struct kobject *kobj, + struct attribute *attr, char *buff, + size_t count) +{ + struct net_device *net_dev = batadv_kobj_to_netdev(kobj); + struct batadv_priv *bat_priv = netdev_priv(net_dev); + uint32_t mark, mask; + char *mask_ptr; + + mask_ptr = strchr(buff, '/'); + if (!mask_ptr) + return -EINVAL; + + *mask_ptr = '\0'; + mask_ptr++; + + /* the mark can be entered in any base */ + if (kstrtou32(buff, 0, &mark) < 0) + return -EINVAL; + + /* the mask must be entered in hex base as it is going to be a bitmask + * and not a prefix length + */ + if (kstrtou32(mask_ptr, 16, &mask) < 0) + return -EINVAL; + + bat_priv->isolation_mark_mask = mask; + /* erase bits not covered by the mask */ + bat_priv->isolation_mark = mark & bat_priv->isolation_mark_mask; + + batadv_info(net_dev, + "New skb mark for extended isolation: %#.8x/%#.8x\n", + bat_priv->isolation_mark, bat_priv->isolation_mark_mask); + + return count; +} + +/** + * batadv_show_isolation_mark - print the current isolation mark/mask + * @kobj: kobject representing the private mesh sysfs directory + * @attr: the batman-adv attribute the user is interacting with + * @buf: the buffer that will contain the data to send back to the user + * + * Returns the number of bytes written into 'buf' on success or a negative error + * code in case of failure + */ +static ssize_t batadv_show_isolation_mark(struct kobject *kobj, + struct attribute *attr, char *buff) +{ + struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj); + + return sprintf(buff, "%#.8x/%#.8x\n", bat_priv->isolation_mark, + bat_priv->isolation_mark_mask); +} + BATADV_ATTR_SIF_BOOL(aggregated_ogms, S_IRUGO | S_IWUSR, NULL); BATADV_ATTR_SIF_BOOL(bonding, S_IRUGO | S_IWUSR, NULL); #ifdef CONFIG_BATMAN_ADV_BLA @@ -475,6 +540,8 @@ BATADV_ATTR_SIF_UINT(log_level, S_IRUGO | S_IWUSR, 0, BATADV_DBG_ALL, NULL); BATADV_ATTR_SIF_BOOL(network_coding, S_IRUGO | S_IWUSR, batadv_nc_status_update); #endif +static BATADV_ATTR(isolation_mark, S_IRUGO | S_IWUSR, + batadv_show_isolation_mark, batadv_store_isolation_mark);
static struct batadv_attribute *batadv_mesh_attrs[] = { &batadv_attr_aggregated_ogms, @@ -498,6 +565,7 @@ static struct batadv_attribute *batadv_mesh_attrs[] = { #ifdef CONFIG_BATMAN_ADV_NC &batadv_attr_network_coding, #endif + &batadv_attr_isolation_mark, NULL, };
diff --git a/types.h b/types.h index a87bc10..0504040 100644 --- a/types.h +++ b/types.h @@ -706,6 +706,8 @@ struct batadv_priv { #ifdef CONFIG_BATMAN_ADV_DEBUG atomic_t log_level; #endif + uint32_t isolation_mark; + uint32_t isolation_mark_mask; atomic_t bcast_seqno; atomic_t bcast_queue_left; atomic_t batman_queue_left;
From: Antonio Quartulli antonio@open-mesh.com
A client sending packets which mark matches the value configured via sysfs has to be identified as isolated using the TT_CLIENT_ISOLA flag.
The match is mask based, meaning that only bits set in the mask are compared with those in the mark value.
If the configured mask is equal to 0 no operation is performed.
Such flag is then advertised within the classic client announcement mechanism.
Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- main.h | 2 ++ multicast.c | 3 ++- packet.h | 1 + soft-interface.c | 7 ++++--- translation-table.c | 14 +++++++++++++- translation-table.h | 2 +- 6 files changed, 23 insertions(+), 6 deletions(-)
diff --git a/main.h b/main.h index 5686bd1..744af47 100644 --- a/main.h +++ b/main.h @@ -67,6 +67,8 @@
#define BATADV_NULL_IFINDEX 0 /* dummy ifindex used to avoid iface checks */
+#define BATADV_NO_MARK 0 + #define BATADV_NUM_WORDS BITS_TO_LONGS(BATADV_TQ_LOCAL_WINDOW_SIZE)
#define BATADV_LOG_BUF_LEN 8192 /* has to be a power of 2 */ diff --git a/multicast.c b/multicast.c index cb1984d..3d479b6 100644 --- a/multicast.c +++ b/multicast.c @@ -145,7 +145,8 @@ static void batadv_mcast_mla_tt_add(struct batadv_priv *bat_priv, continue;
batadv_tt_local_add(bat_priv->soft_iface, mcast_entry->addr, - BATADV_NO_FLAGS, BATADV_NULL_IFINDEX); + BATADV_NO_FLAGS, BATADV_NULL_IFINDEX, + BATADV_NO_MARK); hlist_del(&mcast_entry->list); hlist_add_head(&mcast_entry->list, &bat_priv->mcast.mla_list); } diff --git a/packet.h b/packet.h index cbebac6..5f402c9 100644 --- a/packet.h +++ b/packet.h @@ -112,6 +112,7 @@ enum batadv_tt_client_flags { BATADV_TT_CLIENT_DEL = BIT(0), BATADV_TT_CLIENT_ROAM = BIT(1), BATADV_TT_CLIENT_WIFI = BIT(4), + BATADV_TT_CLIENT_ISOLA = BIT(5), BATADV_TT_CLIENT_NOPURGE = BIT(8), BATADV_TT_CLIENT_NEW = BIT(9), BATADV_TT_CLIENT_PENDING = BIT(10), diff --git a/soft-interface.c b/soft-interface.c index c945cea..2d629ee 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -116,7 +116,7 @@ static int batadv_interface_set_mac_addr(struct net_device *dev, void *p) batadv_tt_local_remove(bat_priv, old_addr, BATADV_NO_FLAGS, "mac address changed", false); batadv_tt_local_add(dev, addr->sa_data, BATADV_NO_FLAGS, - BATADV_NULL_IFINDEX); + BATADV_NULL_IFINDEX, BATADV_NO_MARK); }
return 0; @@ -196,7 +196,8 @@ static int batadv_interface_tx(struct sk_buff *skb, /* Register the client MAC in the transtable */ if (!is_multicast_ether_addr(ethhdr->h_source)) { client_added = batadv_tt_local_add(soft_iface, ethhdr->h_source, - vid, skb->skb_iif); + vid, skb->skb_iif, + skb->mark); if (!client_added) goto dropped; } @@ -480,7 +481,7 @@ int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid) */ batadv_tt_local_add(bat_priv->soft_iface, bat_priv->soft_iface->dev_addr, vid, - BATADV_NULL_IFINDEX); + BATADV_NULL_IFINDEX, BATADV_NO_MARK);
spin_lock_bh(&bat_priv->softif_vlan_list_lock); hlist_add_head_rcu(&vlan->list, &bat_priv->softif_vlan_list); diff --git a/translation-table.c b/translation-table.c index 9eaadbb..373997b 100644 --- a/translation-table.c +++ b/translation-table.c @@ -475,7 +475,7 @@ static void batadv_tt_global_free(struct batadv_priv *bat_priv, * Returns true if the client was successfully added, false otherwise. */ bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, - unsigned short vid, int ifindex) + unsigned short vid, int ifindex, uint32_t mark) { struct batadv_priv *bat_priv = netdev_priv(soft_iface); struct batadv_tt_local_entry *tt_local; @@ -486,6 +486,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, int hash_added, table_size, packet_size_max; bool ret = false, roamed_back = false; uint8_t remote_flags; + uint32_t match_mark;
if (ifindex != BATADV_NULL_IFINDEX) in_dev = dev_get_by_index(&init_net, ifindex); @@ -615,6 +616,17 @@ check_roaming: else tt_local->common.flags &= ~BATADV_TT_CLIENT_WIFI;
+ /* check the mark in the skb: if it's equal to the configured + * isolation_mark, it means the packet is coming from an isolated + * non-mesh client + */ + match_mark = (mark & bat_priv->isolation_mark_mask); + if (bat_priv->isolation_mark_mask && + match_mark == bat_priv->isolation_mark) + tt_local->common.flags |= BATADV_TT_CLIENT_ISOLA; + else + tt_local->common.flags &= ~BATADV_TT_CLIENT_ISOLA; + /* if any "dynamic" flag has been modified, resend an ADD event for this * entry so that all the nodes can get the new flags */ diff --git a/translation-table.h b/translation-table.h index 270773e..202c289 100644 --- a/translation-table.h +++ b/translation-table.h @@ -17,7 +17,7 @@
int batadv_tt_init(struct batadv_priv *bat_priv); bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, - unsigned short vid, int ifindex); + unsigned short vid, int ifindex, uint32_t mark); uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv, const uint8_t *addr, unsigned short vid, const char *message, bool roaming);
From: Antonio Quartulli antonio@open-mesh.com
Print the new BATADV_TT_CLIENT_ISOLA flag properly in the Local and Global Translation Table output.
The character 'I' is used in the flags column to indicate that the entry is marked as isolated.
Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- translation-table.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/translation-table.c b/translation-table.c index 373997b..98655be 100644 --- a/translation-table.c +++ b/translation-table.c @@ -888,7 +888,7 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) "Locally retrieved addresses (from %s) announced via TT (TTVN: %u):\n", net_dev->name, (uint8_t)atomic_read(&bat_priv->tt.vn)); seq_puts(seq, - " Client VID Flags Last seen (CRC )\n"); + " Client VID Flags Last seen (CRC )\n");
for (i = 0; i < hash->size; i++) { head = &hash->table[i]; @@ -915,7 +915,7 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) }
seq_printf(seq, - " * %pM %4i [%c%c%c%c%c] %3u.%03u (%#.8x)\n", + " * %pM %4i [%c%c%c%c%c%c] %3u.%03u (%#.8x)\n", tt_common_entry->addr, BATADV_PRINT_VID(tt_common_entry->vid), (tt_common_entry->flags & @@ -927,6 +927,8 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) BATADV_TT_CLIENT_PENDING ? 'X' : '.'), (tt_common_entry->flags & BATADV_TT_CLIENT_WIFI ? 'W' : '.'), + (tt_common_entry->flags & + BATADV_TT_CLIENT_ISOLA ? 'I' : '.'), no_purge ? 0 : last_seen_secs, no_purge ? 0 : last_seen_msecs, vlan->tt.crc); @@ -1464,13 +1466,14 @@ batadv_tt_global_print_entry(struct batadv_priv *bat_priv,
last_ttvn = atomic_read(&best_entry->orig_node->last_ttvn); seq_printf(seq, - " %c %pM %4i (%3u) via %pM (%3u) (%#.8x) [%c%c%c]\n", + " %c %pM %4i (%3u) via %pM (%3u) (%#.8x) [%c%c%c%c]\n", '*', tt_global_entry->common.addr, BATADV_PRINT_VID(tt_global_entry->common.vid), best_entry->ttvn, best_entry->orig_node->orig, last_ttvn, vlan->tt.crc, (flags & BATADV_TT_CLIENT_ROAM ? 'R' : '.'), (flags & BATADV_TT_CLIENT_WIFI ? 'W' : '.'), + (flags & BATADV_TT_CLIENT_ISOLA ? 'I' : '.'), (flags & BATADV_TT_CLIENT_TEMP ? 'T' : '.'));
batadv_orig_node_vlan_free_ref(vlan); @@ -1495,13 +1498,14 @@ print_list:
last_ttvn = atomic_read(&orig_entry->orig_node->last_ttvn); seq_printf(seq, - " %c %pM %4d (%3u) via %pM (%3u) (%#.8x) [%c%c%c]\n", + " %c %pM %4d (%3u) via %pM (%3u) (%#.8x) [%c%c%c%c]\n", '+', tt_global_entry->common.addr, BATADV_PRINT_VID(tt_global_entry->common.vid), orig_entry->ttvn, orig_entry->orig_node->orig, last_ttvn, vlan->tt.crc, (flags & BATADV_TT_CLIENT_ROAM ? 'R' : '.'), (flags & BATADV_TT_CLIENT_WIFI ? 'W' : '.'), + (flags & BATADV_TT_CLIENT_ISOLA ? 'I' : '.'), (flags & BATADV_TT_CLIENT_TEMP ? 'T' : '.'));
batadv_orig_node_vlan_free_ref(vlan);
From: Antonio Quartulli antonio@open-mesh.com
Change the AP isolation mechanism to not only "isolate" WIFI clients but also all those marked with the more generic "isolation flag" (BATADV_TT_CLIENT_ISOLA).
The result is that when AP isolation is on any unicast packet originated by an "isolated" client and directed to another "isolated" client is dropped at the source node.
Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- translation-table.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/translation-table.c b/translation-table.c index 98655be..2a546e2 100644 --- a/translation-table.c +++ b/translation-table.c @@ -1873,6 +1873,11 @@ _batadv_is_ap_isolated(struct batadv_tt_local_entry *tt_local_entry, tt_global_entry->common.flags & BATADV_TT_CLIENT_WIFI) ret = true;
+ /* check if the two clients are marked as isolated */ + if (tt_local_entry->common.flags & BATADV_TT_CLIENT_ISOLA && + tt_global_entry->common.flags & BATADV_TT_CLIENT_ISOLA) + ret = true; + return ret; }
From: Antonio Quartulli antonio@open-mesh.com
If a broadcast packet is coming from a client marked as isolated, then mark the skb using the isolation mark so that netfilter (or any other application) can recognise them.
The mark is written in the skb based on the mask value: only bits set in the mask are substitued by those in the mark value
Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- soft-interface.c | 14 ++++++++++++-- translation-table.c | 26 ++++++++++++++++++++++++++ translation-table.h | 2 ++ 3 files changed, 40 insertions(+), 2 deletions(-)
diff --git a/soft-interface.c b/soft-interface.c index 2d629ee..d93144f 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -390,9 +390,19 @@ void batadv_interface_rx(struct net_device *soft_iface, batadv_tt_add_temporary_global_entry(bat_priv, orig_node, ethhdr->h_source, vid);
- if (batadv_is_ap_isolated(bat_priv, ethhdr->h_source, ethhdr->h_dest, - vid)) + if (is_multicast_ether_addr(ethhdr->h_dest)) { + if (batadv_tt_global_is_isolated(bat_priv, ethhdr->h_source, + vid)) { + /* save bits in skb->mark not covered by the mask and + * apply the mark on the rest + */ + skb->mark &= ~bat_priv->isolation_mark_mask; + skb->mark |= bat_priv->isolation_mark; + } + } else if (batadv_is_ap_isolated(bat_priv, ethhdr->h_source, + ethhdr->h_dest, vid)) { goto dropped; + }
netif_rx(skb); goto out; diff --git a/translation-table.c b/translation-table.c index 2a546e2..eda84aa 100644 --- a/translation-table.c +++ b/translation-table.c @@ -3596,3 +3596,29 @@ int batadv_tt_init(struct batadv_priv *bat_priv)
return 1; } + +/** + * batadv_tt_global_is_isolated - check if a client is marked as isolated + * @bat_priv: the bat priv with all the soft interface information + * @addr: the mac address of the client + * @vid: the identifier of the VLAN where this client is connected + * + * Return true if the client is marked with the TT_CLIENT_ISOLA flag, flase + * otherwise + */ +bool batadv_tt_global_is_isolated(struct batadv_priv *bat_priv, + const uint8_t *addr, unsigned short vid) +{ + struct batadv_tt_global_entry *tt; + bool ret; + + tt = batadv_tt_global_hash_find(bat_priv, addr, vid); + if (!tt) + return false; + + ret = tt->common.flags & BATADV_TT_CLIENT_ISOLA; + + batadv_tt_global_entry_free_ref(tt); + + return ret; +} diff --git a/translation-table.h b/translation-table.h index 202c289..3ab8a7b 100644 --- a/translation-table.h +++ b/translation-table.h @@ -45,5 +45,7 @@ bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, const unsigned char *addr, unsigned short vid); +bool batadv_tt_global_is_isolated(struct batadv_priv *bat_priv, + const uint8_t *addr, unsigned short vid);
#endif /* _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ */
b.a.t.m.a.n@lists.open-mesh.org