diff --git a/drivers/staging/batman-adv/bat_debugfs.c b/drivers/staging/batman-adv/bat_debugfs.c index 57f84a9f8234a1bc2c5f8571b91bcd7fba24f623..22f3eb98470f956fd39d1bcd8aaf685636b406ae 100644 --- a/drivers/staging/batman-adv/bat_debugfs.c +++ b/drivers/staging/batman-adv/bat_debugfs.c @@ -27,6 +27,7 @@ #include "translation-table.h" #include "originator.h" #include "hard-interface.h" +#include "soft-interface.h" #include "vis.h" #include "icmp_socket.h" @@ -227,6 +228,12 @@ static int originators_open(struct inode *inode, struct file *file) return single_open(file, orig_seq_print_text, net_dev); } +static int softif_neigh_open(struct inode *inode, struct file *file) +{ + struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, softif_neigh_seq_print_text, net_dev); +} + static int transtable_global_open(struct inode *inode, struct file *file) { struct net_device *net_dev = (struct net_device *)inode->i_private; @@ -263,12 +270,14 @@ struct bat_debuginfo bat_debuginfo_##_name = { \ }; static BAT_DEBUGINFO(originators, S_IRUGO, originators_open); +static BAT_DEBUGINFO(softif_neigh, S_IRUGO, softif_neigh_open); static BAT_DEBUGINFO(transtable_global, S_IRUGO, transtable_global_open); static BAT_DEBUGINFO(transtable_local, S_IRUGO, transtable_local_open); static BAT_DEBUGINFO(vis_data, S_IRUGO, vis_data_open); static struct bat_debuginfo *mesh_debuginfos[] = { &bat_debuginfo_originators, + &bat_debuginfo_softif_neigh, &bat_debuginfo_transtable_global, &bat_debuginfo_transtable_local, &bat_debuginfo_vis_data, diff --git a/drivers/staging/batman-adv/main.c b/drivers/staging/batman-adv/main.c index 0587940d27238146a67ed765482877557f445206..f7d6733e3d9adcfe8c30b9ff82c96bed19c6f829 100644 --- a/drivers/staging/batman-adv/main.c +++ b/drivers/staging/batman-adv/main.c @@ -86,9 +86,11 @@ int mesh_init(struct net_device *soft_iface) spin_lock_init(&bat_priv->hna_ghash_lock); spin_lock_init(&bat_priv->vis_hash_lock); spin_lock_init(&bat_priv->vis_list_lock); + spin_lock_init(&bat_priv->softif_neigh_lock); INIT_HLIST_HEAD(&bat_priv->forw_bat_list); INIT_HLIST_HEAD(&bat_priv->forw_bcast_list); + INIT_HLIST_HEAD(&bat_priv->softif_neigh_list); if (originator_init(bat_priv) < 1) goto err; @@ -132,6 +134,8 @@ void mesh_free(struct net_device *soft_iface) hna_local_free(bat_priv); hna_global_free(bat_priv); + softif_neigh_purge(bat_priv); + atomic_set(&bat_priv->mesh_state, MESH_INACTIVE); } diff --git a/drivers/staging/batman-adv/main.h b/drivers/staging/batman-adv/main.h index 5e3f51681f5e7ac5e8356d9269f6f9ffbb346892..ec35ef8920bdb3d9833a4bdb9ac2ea6bfdc292ae 100644 --- a/drivers/staging/batman-adv/main.h +++ b/drivers/staging/batman-adv/main.h @@ -71,6 +71,8 @@ * forw_packet->direct_link_flags */ #define MAX_AGGREGATION_MS 100 +#define SOFTIF_NEIGH_TIMEOUT 180000 /* 3 minutes */ + #define RESET_PROTECTION_MS 30000 #define EXPECTED_SEQNO_RANGE 65536 /* don't reset again within 30 seconds */ diff --git a/drivers/staging/batman-adv/originator.c b/drivers/staging/batman-adv/originator.c index 55270080a44bf3de5caffad4eb433267bc0d7e96..fc7fb31b5eda6a464c6eebfed286f20538fe75fb 100644 --- a/drivers/staging/batman-adv/originator.c +++ b/drivers/staging/batman-adv/originator.c @@ -28,6 +28,7 @@ #include "routing.h" #include "hard-interface.h" #include "unicast.h" +#include "soft-interface.h" static void purge_orig(struct work_struct *work); @@ -286,6 +287,7 @@ static void _purge_orig(struct bat_priv *bat_priv) spin_unlock_irqrestore(&bat_priv->orig_hash_lock, flags); + softif_neigh_purge(bat_priv); } static void purge_orig(struct work_struct *work) diff --git a/drivers/staging/batman-adv/routing.c b/drivers/staging/batman-adv/routing.c index 750cec708244c7476c4e34995bee30b3e4bc139a..3946c7c1b8e712586fb66ef582d181a71c861850 100644 --- a/drivers/staging/batman-adv/routing.c +++ b/drivers/staging/batman-adv/routing.c @@ -1117,8 +1117,8 @@ static int check_unicast_packet(struct sk_buff *skb, int hdr_size) return 0; } -static int route_unicast_packet(struct sk_buff *skb, - struct batman_if *recv_if, int hdr_size) +int route_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if, + int hdr_size) { struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); struct orig_node *orig_node; @@ -1186,7 +1186,7 @@ static int route_unicast_packet(struct sk_buff *skb, return NET_RX_SUCCESS; skb = new_skb; - unicast_packet = (struct unicast_packet *) skb->data; + unicast_packet = (struct unicast_packet *)skb->data; } /* decrement ttl */ @@ -1210,7 +1210,7 @@ int recv_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if) /* packet for me */ if (is_my_mac(unicast_packet->dest)) { - interface_rx(recv_if->soft_iface, skb, hdr_size); + interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size); return NET_RX_SUCCESS; } @@ -1242,7 +1242,7 @@ int recv_ucast_frag_packet(struct sk_buff *skb, struct batman_if *recv_if) if (!new_skb) return NET_RX_SUCCESS; - interface_rx(recv_if->soft_iface, new_skb, + interface_rx(recv_if->soft_iface, new_skb, recv_if, sizeof(struct unicast_packet)); return NET_RX_SUCCESS; } @@ -1324,7 +1324,7 @@ int recv_bcast_packet(struct sk_buff *skb, struct batman_if *recv_if) add_bcast_packet_to_list(bat_priv, skb); /* broadcast for me */ - interface_rx(recv_if->soft_iface, skb, hdr_size); + interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size); return NET_RX_SUCCESS; } diff --git a/drivers/staging/batman-adv/routing.h b/drivers/staging/batman-adv/routing.h index 92674c8d9c030c96b7da1bf39ee2c31eeae33688..f108f230bfdbb88476a55a8278b8001f725f0620 100644 --- a/drivers/staging/batman-adv/routing.h +++ b/drivers/staging/batman-adv/routing.h @@ -32,6 +32,8 @@ void receive_bat_packet(struct ethhdr *ethhdr, void update_routes(struct bat_priv *bat_priv, struct orig_node *orig_node, struct neigh_node *neigh_node, unsigned char *hna_buff, int hna_buff_len); +int route_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if, + int hdr_size); int recv_icmp_packet(struct sk_buff *skb, struct batman_if *recv_if); int recv_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if); int recv_ucast_frag_packet(struct sk_buff *skb, struct batman_if *recv_if); diff --git a/drivers/staging/batman-adv/soft-interface.c b/drivers/staging/batman-adv/soft-interface.c index 0e996181daf764b81965700b8b0dd90a0e39316d..c3313d0616cce98e0ad82b31b185ba267608c322 100644 --- a/drivers/staging/batman-adv/soft-interface.c +++ b/drivers/staging/batman-adv/soft-interface.c @@ -33,7 +33,9 @@ #include #include #include +#include #include "unicast.h" +#include "routing.h" static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd); @@ -75,6 +77,220 @@ int my_skb_head_push(struct sk_buff *skb, unsigned int len) return 0; } +static void softif_neigh_free_ref(struct kref *refcount) +{ + struct softif_neigh *softif_neigh; + + softif_neigh = container_of(refcount, struct softif_neigh, refcount); + kfree(softif_neigh); +} + +static void softif_neigh_free_rcu(struct rcu_head *rcu) +{ + struct softif_neigh *softif_neigh; + + softif_neigh = container_of(rcu, struct softif_neigh, rcu); + kref_put(&softif_neigh->refcount, softif_neigh_free_ref); +} + +void softif_neigh_purge(struct bat_priv *bat_priv) +{ + struct softif_neigh *softif_neigh, *softif_neigh_tmp; + struct hlist_node *node, *node_tmp; + unsigned long flags; + + spin_lock_irqsave(&bat_priv->softif_neigh_lock, flags); + + hlist_for_each_entry_safe(softif_neigh, node, node_tmp, + &bat_priv->softif_neigh_list, list) { + + if ((!time_after(jiffies, softif_neigh->last_seen + + msecs_to_jiffies(SOFTIF_NEIGH_TIMEOUT))) && + (atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE)) + continue; + + hlist_del_rcu(&softif_neigh->list); + + if (bat_priv->softif_neigh == softif_neigh) { + bat_dbg(DBG_ROUTES, bat_priv, + "Current mesh exit point '%pM' vanished " + "(vid: %d).\n", + softif_neigh->addr, softif_neigh->vid); + softif_neigh_tmp = bat_priv->softif_neigh; + bat_priv->softif_neigh = NULL; + kref_put(&softif_neigh_tmp->refcount, + softif_neigh_free_ref); + } + + call_rcu(&softif_neigh->rcu, softif_neigh_free_rcu); + } + + spin_unlock_irqrestore(&bat_priv->softif_neigh_lock, flags); +} + +static struct softif_neigh *softif_neigh_get(struct bat_priv *bat_priv, + uint8_t *addr, short vid) +{ + struct softif_neigh *softif_neigh; + struct hlist_node *node; + unsigned long flags; + + rcu_read_lock(); + hlist_for_each_entry_rcu(softif_neigh, node, + &bat_priv->softif_neigh_list, list) { + if (memcmp(softif_neigh->addr, addr, ETH_ALEN) != 0) + continue; + + if (softif_neigh->vid != vid) + continue; + + softif_neigh->last_seen = jiffies; + goto found; + } + + softif_neigh = kzalloc(sizeof(struct softif_neigh), GFP_ATOMIC); + if (!softif_neigh) + goto out; + + memcpy(softif_neigh->addr, addr, ETH_ALEN); + softif_neigh->vid = vid; + softif_neigh->last_seen = jiffies; + kref_init(&softif_neigh->refcount); + + INIT_HLIST_NODE(&softif_neigh->list); + spin_lock_irqsave(&bat_priv->softif_neigh_lock, flags); + hlist_add_head_rcu(&softif_neigh->list, &bat_priv->softif_neigh_list); + spin_unlock_irqrestore(&bat_priv->softif_neigh_lock, flags); + +found: + kref_get(&softif_neigh->refcount); +out: + rcu_read_unlock(); + return softif_neigh; +} + +int softif_neigh_seq_print_text(struct seq_file *seq, void *offset) +{ + struct net_device *net_dev = (struct net_device *)seq->private; + struct bat_priv *bat_priv = netdev_priv(net_dev); + struct softif_neigh *softif_neigh; + struct hlist_node *node; + size_t buf_size, pos; + char *buff; + + if (!bat_priv->primary_if) { + return seq_printf(seq, "BATMAN mesh %s disabled - " + "please specify interfaces to enable it\n", + net_dev->name); + } + + seq_printf(seq, "Softif neighbor list (%s)\n", net_dev->name); + + buf_size = 1; + /* Estimate length for: " xx:xx:xx:xx:xx:xx\n" */ + rcu_read_lock(); + hlist_for_each_entry_rcu(softif_neigh, node, + &bat_priv->softif_neigh_list, list) + buf_size += 30; + rcu_read_unlock(); + + buff = kmalloc(buf_size, GFP_ATOMIC); + if (!buff) + return -ENOMEM; + + buff[0] = '\0'; + pos = 0; + + rcu_read_lock(); + hlist_for_each_entry_rcu(softif_neigh, node, + &bat_priv->softif_neigh_list, list) { + pos += snprintf(buff + pos, 31, "%s %pM (vid: %d)\n", + bat_priv->softif_neigh == softif_neigh + ? "=>" : " ", softif_neigh->addr, + softif_neigh->vid); + } + rcu_read_unlock(); + + seq_printf(seq, "%s", buff); + kfree(buff); + return 0; +} + +static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev, + short vid) +{ + struct bat_priv *bat_priv = netdev_priv(dev); + struct ethhdr *ethhdr = (struct ethhdr *)skb->data; + struct batman_packet *batman_packet; + struct softif_neigh *softif_neigh, *softif_neigh_tmp; + + if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) + batman_packet = (struct batman_packet *) + (skb->data + ETH_HLEN + VLAN_HLEN); + else + batman_packet = (struct batman_packet *)(skb->data + ETH_HLEN); + + if (batman_packet->version != COMPAT_VERSION) + goto err; + + if (batman_packet->packet_type != BAT_PACKET) + goto err; + + if (!(batman_packet->flags & PRIMARIES_FIRST_HOP)) + goto err; + + if (is_my_mac(batman_packet->orig)) + goto err; + + softif_neigh = softif_neigh_get(bat_priv, batman_packet->orig, vid); + + if (!softif_neigh) + goto err; + + if (bat_priv->softif_neigh == softif_neigh) + goto out; + + /* we got a neighbor but its mac is 'bigger' than ours */ + if (memcmp(bat_priv->primary_if->net_dev->dev_addr, + softif_neigh->addr, ETH_ALEN) < 0) + goto out; + + /* switch to new 'smallest neighbor' */ + if ((bat_priv->softif_neigh) && + (memcmp(softif_neigh->addr, bat_priv->softif_neigh->addr, + ETH_ALEN) < 0)) { + bat_dbg(DBG_ROUTES, bat_priv, + "Changing mesh exit point from %pM (vid: %d) " + "to %pM (vid: %d).\n", + bat_priv->softif_neigh->addr, + bat_priv->softif_neigh->vid, + softif_neigh->addr, softif_neigh->vid); + softif_neigh_tmp = bat_priv->softif_neigh; + bat_priv->softif_neigh = softif_neigh; + kref_put(&softif_neigh_tmp->refcount, softif_neigh_free_ref); + /* we need to hold the additional reference */ + goto err; + } + + /* close own batX device and use softif_neigh as exit node */ + if ((!bat_priv->softif_neigh) && + (memcmp(softif_neigh->addr, + bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN) < 0)) { + bat_dbg(DBG_ROUTES, bat_priv, + "Setting mesh exit point to %pM (vid: %d).\n", + softif_neigh->addr, softif_neigh->vid); + bat_priv->softif_neigh = softif_neigh; + /* we need to hold the additional reference */ + goto err; + } + +out: + kref_put(&softif_neigh->refcount, softif_neigh_free_ref); +err: + kfree_skb(skb); + return; +} + static int interface_open(struct net_device *dev) { netif_start_queue(dev); @@ -109,7 +325,6 @@ static int interface_set_mac_addr(struct net_device *dev, void *p) } memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); - return 0; } @@ -129,13 +344,36 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) struct ethhdr *ethhdr = (struct ethhdr *)skb->data; struct bat_priv *bat_priv = netdev_priv(soft_iface); struct bcast_packet *bcast_packet; + struct vlan_ethhdr *vhdr; int data_len = skb->len, ret; + short vid = -1; if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE) goto dropped; soft_iface->trans_start = jiffies; + switch (ntohs(ethhdr->h_proto)) { + case ETH_P_8021Q: + vhdr = (struct vlan_ethhdr *)skb->data; + vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK; + + if (ntohs(vhdr->h_vlan_encapsulated_proto) != ETH_P_BATMAN) + break; + + /* fall through */ + case ETH_P_BATMAN: + softif_batman_recv(skb, soft_iface, vid); + goto end; + } + + /** + * if we have a another chosen mesh exit node in range + * it will transport the packets to the mesh + */ + if ((bat_priv->softif_neigh) && (bat_priv->softif_neigh->vid == vid)) + goto dropped; + /* TODO: check this for locks */ hna_local_add(soft_iface, ethhdr->h_source); @@ -189,16 +427,60 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) } void interface_rx(struct net_device *soft_iface, - struct sk_buff *skb, int hdr_size) + struct sk_buff *skb, struct batman_if *recv_if, + int hdr_size) { - struct bat_priv *priv = netdev_priv(soft_iface); + struct bat_priv *bat_priv = netdev_priv(soft_iface); + struct unicast_packet *unicast_packet; + struct ethhdr *ethhdr; + struct vlan_ethhdr *vhdr; + short vid = -1; + int ret; /* check if enough space is available for pulling, and pull */ if (!pskb_may_pull(skb, hdr_size)) goto dropped; skb_pull_rcsum(skb, hdr_size); -/* skb_set_mac_header(skb, -sizeof(struct ethhdr));*/ + skb_reset_mac_header(skb); + + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + switch (ntohs(ethhdr->h_proto)) { + case ETH_P_8021Q: + vhdr = (struct vlan_ethhdr *)skb->data; + vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK; + + if (ntohs(vhdr->h_vlan_encapsulated_proto) != ETH_P_BATMAN) + break; + + /* fall through */ + case ETH_P_BATMAN: + goto dropped; + } + + /** + * if we have a another chosen mesh exit node in range + * it will transport the packets to the non-mesh network + */ + if ((bat_priv->softif_neigh) && (bat_priv->softif_neigh->vid == vid)) { + skb_push(skb, hdr_size); + unicast_packet = (struct unicast_packet *)skb->data; + + if ((unicast_packet->packet_type != BAT_UNICAST) && + (unicast_packet->packet_type != BAT_UNICAST_FRAG)) + goto dropped; + + skb_reset_mac_header(skb); + + memcpy(unicast_packet->dest, + bat_priv->softif_neigh->addr, ETH_ALEN); + ret = route_unicast_packet(skb, recv_if, hdr_size); + if (ret == NET_RX_DROP) + goto dropped; + + goto out; + } /* skb->dev & skb->pkt_type are set here */ if (unlikely(!pskb_may_pull(skb, ETH_HLEN))) @@ -211,8 +493,8 @@ void interface_rx(struct net_device *soft_iface, /* skb->ip_summed = CHECKSUM_UNNECESSARY;*/ - priv->stats.rx_packets++; - priv->stats.rx_bytes += skb->len + sizeof(struct ethhdr); + bat_priv->stats.rx_packets++; + bat_priv->stats.rx_bytes += skb->len + sizeof(struct ethhdr); soft_iface->last_rx = jiffies; @@ -221,6 +503,7 @@ void interface_rx(struct net_device *soft_iface, dropped: kfree_skb(skb); +out: return; } @@ -310,6 +593,7 @@ struct net_device *softif_create(char *name) bat_priv->primary_if = NULL; bat_priv->num_ifaces = 0; + bat_priv->softif_neigh = NULL; ret = sysfs_add_meshif(soft_iface); if (ret < 0) diff --git a/drivers/staging/batman-adv/soft-interface.h b/drivers/staging/batman-adv/soft-interface.h index 843a7ec082fed935ce4a80d8fe8a22def227ce63..02b77334d10d5889ba72077aa6db29b4e1c8dfb3 100644 --- a/drivers/staging/batman-adv/soft-interface.h +++ b/drivers/staging/batman-adv/soft-interface.h @@ -23,9 +23,12 @@ #define _NET_BATMAN_ADV_SOFT_INTERFACE_H_ int my_skb_head_push(struct sk_buff *skb, unsigned int len); +int softif_neigh_seq_print_text(struct seq_file *seq, void *offset); +void softif_neigh_purge(struct bat_priv *bat_priv); int interface_tx(struct sk_buff *skb, struct net_device *soft_iface); void interface_rx(struct net_device *soft_iface, - struct sk_buff *skb, int hdr_size); + struct sk_buff *skb, struct batman_if *recv_if, + int hdr_size); struct net_device *softif_create(char *name); void softif_destroy(struct net_device *soft_iface); diff --git a/drivers/staging/batman-adv/types.h b/drivers/staging/batman-adv/types.h index 8f6ba1c5692761348a5dfd8339e84432c2054c9c..835d51f9a95d10383e7a446cc8c08c9ee9840b29 100644 --- a/drivers/staging/batman-adv/types.h +++ b/drivers/staging/batman-adv/types.h @@ -123,6 +123,8 @@ struct bat_priv { atomic_t bcast_queue_left; atomic_t batman_queue_left; char num_ifaces; + struct hlist_head softif_neigh_list; + struct softif_neigh *softif_neigh; struct debug_log *debug_log; struct batman_if *primary_if; struct kobject *mesh_obj; @@ -141,6 +143,7 @@ struct bat_priv { spinlock_t hna_ghash_lock; /* protects hna_global_hash */ spinlock_t vis_hash_lock; /* protects vis_hash */ spinlock_t vis_list_lock; /* protects vis_info::recv_list */ + spinlock_t softif_neigh_lock; /* protects soft-interface neigh list */ int16_t num_local_hna; atomic_t hna_local_changed; struct delayed_work hna_work; @@ -239,4 +242,13 @@ struct recvlist_node { uint8_t mac[ETH_ALEN]; }; +struct softif_neigh { + struct hlist_node list; + uint8_t addr[ETH_ALEN]; + unsigned long last_seen; + short vid; + struct kref refcount; + struct rcu_head rcu; +}; + #endif /* _NET_BATMAN_ADV_TYPES_H_ */