/** * batadv_skb_head_push() - Increase header size and move (push) head pointer * @skb: packet buffer which should be modified * @len: number of bytes to add * * Return: 0 on success or negative error number in case of failure
*/ int batadv_skb_head_push(struct sk_buff *skb, unsignedint len)
{ int result;
/* TODO: We must check if we can release all references to non-payload * data using __skb_header_release in our skbs to allow skb_cow_header * to work optimally. This means that those skbs are not allowed to read * or write any data which is before the current position of skb->data * after that call and thus allow other skbs with the same data buffer * to write freely in that area.
*/
result = skb_cow_head(skb, len); if (result < 0) return result;
skb_push(skb, len); return 0;
}
/** * batadv_sum_counter() - Sum the cpu-local counters for index 'idx' * @bat_priv: the bat priv with all the mesh interface information * @idx: index of counter to sum up * * Return: sum of all cpu-local counters
*/ static u64 batadv_sum_counter(struct batadv_priv *bat_priv, size_t idx)
{
u64 *counters, sum = 0; int cpu;
for_each_possible_cpu(cpu) {
counters = per_cpu_ptr(bat_priv->bat_counters, cpu);
sum += counters[idx];
}
/** * batadv_interface_set_rx_mode() - set the rx mode of a device * @dev: registered network device to modify * * We do not actually need to set any rx filters for the virtual batman * mesh interface. However a dummy handler enables a user to set static * multicast listeners for instance.
*/ staticvoid batadv_interface_set_rx_mode(struct net_device *dev)
{
}
if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) goto dropped;
/* reset control block to avoid left overs from previous users */
memset(skb->cb, 0, sizeof(struct batadv_skb_cb));
netif_trans_update(mesh_iface);
vid = batadv_get_vid(skb, 0);
skb_reset_mac_header(skb);
ethhdr = eth_hdr(skb);
proto = ethhdr->h_proto;
switch (ntohs(proto)) { case ETH_P_8021Q: if (!pskb_may_pull(skb, sizeof(*vhdr))) goto dropped;
vhdr = vlan_eth_hdr(skb);
proto = vhdr->h_vlan_encapsulated_proto;
/* drop batman-in-batman packets to prevent loops */ if (proto != htons(ETH_P_BATMAN)) {
network_offset += VLAN_HLEN; break;
}
fallthrough; case ETH_P_BATMAN: goto dropped;
}
skb_set_network_header(skb, network_offset);
if (batadv_bla_tx(bat_priv, skb, vid)) goto dropped;
/* skb->data might have been reallocated by batadv_bla_tx() */
ethhdr = eth_hdr(skb);
/* Register the client MAC in the transtable */ if (!is_multicast_ether_addr(ethhdr->h_source) &&
!batadv_bla_is_loopdetect_mac(ethhdr->h_source)) {
client_added = batadv_tt_local_add(mesh_iface, ethhdr->h_source,
vid, skb->skb_iif,
skb->mark); if (!client_added) goto dropped;
}
/* Snoop address candidates from DHCPACKs for early DAT filling */
batadv_dat_snoop_outgoing_dhcp_ack(bat_priv, skb, proto, vid);
/* don't accept stp packets. STP does not help in meshes. * better use the bridge loop avoidance ... * * The same goes for ECTP sent at least by some Cisco Switches, * it might confuse the mesh when used with bridge loop avoidance.
*/ if (batadv_compare_eth(ethhdr->h_dest, stp_addr)) goto dropped;
if (batadv_compare_eth(ethhdr->h_dest, ectp_addr)) goto dropped;
gw_mode = atomic_read(&bat_priv->gw.mode); if (is_multicast_ether_addr(ethhdr->h_dest)) { /* if gw mode is off, broadcast every packet */ if (gw_mode == BATADV_GW_MODE_OFF) {
do_bcast = true; goto send;
}
dhcp_rcp = batadv_gw_dhcp_recipient_get(skb, &header_len,
chaddr); /* skb->data may have been modified by * batadv_gw_dhcp_recipient_get()
*/
ethhdr = eth_hdr(skb); /* if gw_mode is on, broadcast any non-DHCP message. * All the DHCP packets are going to be sent as unicast
*/ if (dhcp_rcp == BATADV_DHCP_NO) {
do_bcast = true; goto send;
}
if (dhcp_rcp == BATADV_DHCP_TO_CLIENT)
dst_hint = chaddr; elseif ((gw_mode == BATADV_GW_MODE_SERVER) &&
(dhcp_rcp == BATADV_DHCP_TO_SERVER)) /* gateways should not forward any DHCP message if * directed to a DHCP server
*/ goto dropped;
send: if (do_bcast && !is_broadcast_ether_addr(ethhdr->h_dest)) {
forw_mode = batadv_mcast_forw_mode(bat_priv, skb, vid,
&mcast_is_routable); switch (forw_mode) { case BATADV_FORW_BCAST: break; case BATADV_FORW_UCASTS: case BATADV_FORW_MCAST:
do_bcast = false; break; case BATADV_FORW_NONE:
fallthrough; default: goto dropped;
}
}
}
batadv_skb_set_priority(skb, 0);
/* ethernet packet should be broadcasted */ if (do_bcast) {
primary_if = batadv_primary_if_get_selected(bat_priv); if (!primary_if) goto dropped;
/* in case of ARP request, we do not immediately broadcasti the * packet, instead we first wait for DAT to try to retrieve the * correct ARP entry
*/ if (batadv_dat_snoop_outgoing_arp_request(bat_priv, skb))
brd_delay = msecs_to_jiffies(ARP_REQ_DELAY);
if (batadv_skb_head_push(skb, sizeof(*bcast_packet)) < 0) goto dropped;
/* hw address of first interface is the orig mac because only * this mac is known throughout the mesh
*/
ether_addr_copy(bcast_packet->orig,
primary_if->net_dev->dev_addr);
/* set broadcast sequence number */
seqno = atomic_inc_return(&bat_priv->bcast_seqno);
bcast_packet->seqno = htonl(seqno);
batadv_send_bcast_packet(bat_priv, skb, brd_delay, true); /* unicast packet */
} else { /* DHCP packets going to a server will use the GW feature */ if (dhcp_rcp == BATADV_DHCP_TO_SERVER) {
ret = batadv_gw_out_of_range(bat_priv, skb); if (ret) goto dropped;
ret = batadv_send_skb_via_gw(bat_priv, skb, vid);
} elseif (forw_mode == BATADV_FORW_UCASTS) {
ret = batadv_mcast_forw_send(bat_priv, skb, vid,
mcast_is_routable);
} elseif (forw_mode == BATADV_FORW_MCAST) {
ret = batadv_mcast_forw_mcsend(bat_priv, skb);
} else { if (batadv_dat_snoop_outgoing_arp_request(bat_priv,
skb)) goto dropped;
/** * batadv_interface_rx() - receive ethernet frame on local batman-adv interface * @mesh_iface: local interface which will receive the ethernet frame * @skb: ethernet frame for @mesh_iface * @hdr_size: size of already parsed batman-adv header * @orig_node: originator from which the batman-adv packet was sent * * Sends an ethernet frame to the receive path of the local @mesh_iface. * skb->data has still point to the batman-adv header with the size @hdr_size. * The caller has to have parsed this header already and made sure that at least * @hdr_size bytes are still available for pull in @skb. * * The packet may still get dropped. This can happen when the encapsulated * ethernet frame is invalid or contains again an batman-adv packet. Also * unicast packets will be dropped directly when it was sent between two * isolated clients.
*/ void batadv_interface_rx(struct net_device *mesh_iface, struct sk_buff *skb, int hdr_size, struct batadv_orig_node *orig_node)
{ struct batadv_bcast_packet *batadv_bcast_packet; struct batadv_priv *bat_priv = netdev_priv(mesh_iface); struct vlan_ethhdr *vhdr; struct ethhdr *ethhdr; unsignedshort vid; int packet_type;
/* Let the bridge loop avoidance check the packet. If will * not handle it, we can safely push it up.
*/ if (batadv_bla_rx(bat_priv, skb, vid, packet_type)) goto out;
if (orig_node)
batadv_tt_add_temporary_global_entry(bat_priv, orig_node,
ethhdr->h_source, vid);
if (is_multicast_ether_addr(ethhdr->h_dest)) { /* set the mark on broadcast packets if AP isolation is ON and * the packet is coming from an "isolated" client
*/ if (batadv_vlan_ap_isola_get(bat_priv, vid) &&
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;
}
} elseif (batadv_is_ap_isolated(bat_priv, ethhdr->h_source,
ethhdr->h_dest, vid)) { goto dropped;
}
netif_rx(skb); goto out;
dropped:
kfree_skb(skb);
out: return;
}
/** * batadv_meshif_vlan_release() - release vlan from lists and queue for free * after rcu grace period * @ref: kref pointer of the vlan object
*/ void batadv_meshif_vlan_release(struct kref *ref)
{ struct batadv_meshif_vlan *vlan;
/** * batadv_meshif_vlan_get() - get the vlan object for a specific vid * @bat_priv: the bat priv with all the mesh interface information * @vid: the identifier of the vlan object to retrieve * * Return: the private data of the vlan matching the vid passed as argument or * NULL otherwise. The refcounter of the returned object is incremented by 1.
*/ struct batadv_meshif_vlan *batadv_meshif_vlan_get(struct batadv_priv *bat_priv, unsignedshort vid)
{ struct batadv_meshif_vlan *vlan_tmp, *vlan = NULL;
rcu_read_lock();
hlist_for_each_entry_rcu(vlan_tmp, &bat_priv->meshif_vlan_list, list) { if (vlan_tmp->vid != vid) continue;
if (!kref_get_unless_zero(&vlan_tmp->refcount)) continue;
vlan = vlan_tmp; break;
}
rcu_read_unlock();
return vlan;
}
/** * batadv_meshif_create_vlan() - allocate the needed resources for a new vlan * @bat_priv: the bat priv with all the mesh interface information * @vid: the VLAN identifier * * Return: 0 on success, a negative error otherwise.
*/ int batadv_meshif_create_vlan(struct batadv_priv *bat_priv, unsignedshort vid)
{ struct batadv_meshif_vlan *vlan;
/* add a new TT local entry. This one will be marked with the NOPURGE * flag
*/
batadv_tt_local_add(bat_priv->mesh_iface,
bat_priv->mesh_iface->dev_addr, vid,
BATADV_NULL_IFINDEX, BATADV_NO_MARK);
/* don't return reference to new meshif_vlan */
batadv_meshif_vlan_put(vlan);
return 0;
}
/** * batadv_meshif_destroy_vlan() - remove and destroy a meshif_vlan object * @bat_priv: the bat priv with all the mesh interface information * @vlan: the object to remove
*/ staticvoid batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv, struct batadv_meshif_vlan *vlan)
{ /* explicitly remove the associated TT local entry because it is marked * with the NOPURGE flag
*/
batadv_tt_local_remove(bat_priv, bat_priv->mesh_iface->dev_addr,
vlan->vid, "vlan interface destroyed", false);
batadv_meshif_vlan_put(vlan);
}
/** * batadv_interface_add_vid() - ndo_add_vid API implementation * @dev: the netdev of the mesh interface * @proto: protocol of the vlan id * @vid: identifier of the new vlan * * Set up all the internal structures for handling the new vlan on top of the * mesh interface * * Return: 0 on success or a negative error code in case of failure.
*/ staticint batadv_interface_add_vid(struct net_device *dev, __be16 proto, unsignedshort vid)
{ struct batadv_priv *bat_priv = netdev_priv(dev); struct batadv_meshif_vlan *vlan;
/* only 802.1Q vlans are supported. * batman-adv does not know how to handle other types
*/ if (proto != htons(ETH_P_8021Q)) return -EINVAL;
/* VID 0 is only used to indicate "priority tag" frames which only * contain priority information and no VID. No management structures * should be created for this VID and it should be handled like an * untagged frame.
*/ if (vid == 0) return 0;
vid |= BATADV_VLAN_HAS_TAG;
/* if a new vlan is getting created and it already exists, it means that * it was not deleted yet. batadv_meshif_vlan_get() increases the * refcount in order to revive the object. * * if it does not exist then create it.
*/
vlan = batadv_meshif_vlan_get(bat_priv, vid); if (!vlan) return batadv_meshif_create_vlan(bat_priv, vid);
/* add a new TT local entry. This one will be marked with the NOPURGE * flag. This must be added again, even if the vlan object already * exists, because the entry was deleted by kill_vid()
*/
batadv_tt_local_add(bat_priv->mesh_iface,
bat_priv->mesh_iface->dev_addr, vid,
BATADV_NULL_IFINDEX, BATADV_NO_MARK);
return 0;
}
/** * batadv_interface_kill_vid() - ndo_kill_vid API implementation * @dev: the netdev of the mesh interface * @proto: protocol of the vlan id * @vid: identifier of the deleted vlan * * Destroy all the internal structures used to handle the vlan identified by vid * on top of the mesh interface * * Return: 0 on success, -EINVAL if the specified prototype is not ETH_P_8021Q * or -ENOENT if the specified vlan id wasn't registered.
*/ staticint batadv_interface_kill_vid(struct net_device *dev, __be16 proto, unsignedshort vid)
{ struct batadv_priv *bat_priv = netdev_priv(dev); struct batadv_meshif_vlan *vlan;
/* only 802.1Q vlans are supported. batman-adv does not know how to * handle other types
*/ if (proto != htons(ETH_P_8021Q)) return -EINVAL;
/* "priority tag" frames are handled like "untagged" frames * and no meshif_vlan needs to be destroyed
*/ if (vid == 0) return 0;
vlan = batadv_meshif_vlan_get(bat_priv, vid | BATADV_VLAN_HAS_TAG); if (!vlan) return -ENOENT;
batadv_meshif_destroy_vlan(bat_priv, vlan);
/* finally free the vlan object */
batadv_meshif_vlan_put(vlan);
return 0;
}
/* batman-adv network devices have devices nesting below it and are a special * "super class" of normal network devices; split their locks off into a * separate class since they always nest.
*/ staticstruct lock_class_key batadv_netdev_xmit_lock_key; staticstruct lock_class_key batadv_netdev_addr_lock_key;
/** * batadv_set_lockdep_class_one() - Set lockdep class for a single tx queue * @dev: device which owns the tx queue * @txq: tx queue to modify * @_unused: always NULL
*/ staticvoid batadv_set_lockdep_class_one(struct net_device *dev, struct netdev_queue *txq, void *_unused)
{
lockdep_set_class(&txq->_xmit_lock, &batadv_netdev_xmit_lock_key);
}
/** * batadv_set_lockdep_class() - Set txq and addr_list lockdep class * @dev: network device to modify
*/ staticvoid batadv_set_lockdep_class(struct net_device *dev)
{
lockdep_set_class(&dev->addr_list_lock, &batadv_netdev_addr_lock_key);
netdev_for_each_tx_queue(dev, batadv_set_lockdep_class_one, NULL);
}
/** * batadv_meshif_init_late() - late stage initialization of mesh interface * @dev: registered network device to modify * * Return: error code on failures
*/ staticint batadv_meshif_init_late(struct net_device *dev)
{ struct batadv_priv *bat_priv;
u32 random_seqno; int ret;
size_t cnt_len = sizeof(u64) * BATADV_CNT_NUM;
/* batadv_interface_stats() needs to be available as soon as * register_netdevice() has been called
*/
bat_priv->bat_counters = __alloc_percpu(cnt_len, __alignof__(u64)); if (!bat_priv->bat_counters) return -ENOMEM;
/** * batadv_meshif_slave_add() - Add a slave interface to a batadv_mesh_interface * @dev: batadv_mesh_interface used as master interface * @slave_dev: net_device which should become the slave interface * @extack: extended ACK report struct * * Return: 0 if successful or error otherwise.
*/ staticint batadv_meshif_slave_add(struct net_device *dev, struct net_device *slave_dev, struct netlink_ext_ack *extack)
{ struct batadv_hard_iface *hard_iface; int ret = -EINVAL;
hard_iface = batadv_hardif_get_by_netdev(slave_dev); if (!hard_iface || hard_iface->mesh_iface) goto out;
ret = batadv_hardif_enable_interface(hard_iface, dev);
out:
batadv_hardif_put(hard_iface); return ret;
}
/** * batadv_meshif_slave_del() - Delete a slave iface from a batadv_mesh_interface * @dev: batadv_mesh_interface used as master interface * @slave_dev: net_device which should be removed from the master interface * * Return: 0 if successful or error otherwise.
*/ staticint batadv_meshif_slave_del(struct net_device *dev, struct net_device *slave_dev)
{ struct batadv_hard_iface *hard_iface; int ret = -EINVAL;
/** * batadv_meshif_free() - Deconstructor of batadv_mesh_interface * @dev: Device to cleanup and remove
*/ staticvoid batadv_meshif_free(struct net_device *dev)
{
batadv_mesh_free(dev);
/* some scheduled RCU callbacks need the bat_priv struct to accomplish * their tasks. Wait for them all to be finished before freeing the * netdev and its private data (bat_priv)
*/
rcu_barrier();
}
/** * batadv_meshif_init_early() - early stage initialization of mesh interface * @dev: registered network device to modify
*/ staticvoid batadv_meshif_init_early(struct net_device *dev)
{
ether_setup(dev);
/* can't call min_mtu, because the needed variables * have not been initialized yet
*/
dev->mtu = ETH_DATA_LEN;
dev->max_mtu = BATADV_MAX_MTU;
/* generate random address */
eth_hw_addr_random(dev);
dev->ethtool_ops = &batadv_ethtool_ops;
}
/** * batadv_meshif_validate() - validate configuration of new batadv link * @tb: IFLA_INFO_DATA netlink attributes * @data: enum batadv_ifla_attrs attributes * @extack: extended ACK report struct * * Return: 0 if successful or error otherwise.
*/ staticint batadv_meshif_validate(struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack)
{ struct batadv_algo_ops *algo_ops;
if (!data) return 0;
if (data[IFLA_BATADV_ALGO_NAME]) {
algo_ops = batadv_algo_get(nla_data(data[IFLA_BATADV_ALGO_NAME])); if (!algo_ops) return -EINVAL;
}
return 0;
}
/** * batadv_meshif_newlink() - pre-initialize and register new batadv link * @dev: network device to register * @params: rtnl newlink parameters * @extack: extended ACK report struct * * Return: 0 if successful or error otherwise.
*/ staticint batadv_meshif_newlink(struct net_device *dev, struct rtnl_newlink_params *params, struct netlink_ext_ack *extack)
{ struct batadv_priv *bat_priv = netdev_priv(dev); struct nlattr **data = params->data; constchar *algo_name; int err;
if (data && data[IFLA_BATADV_ALGO_NAME]) {
algo_name = nla_data(data[IFLA_BATADV_ALGO_NAME]);
err = batadv_algo_select(bat_priv, algo_name); if (err) return -EINVAL;
}
return register_netdevice(dev);
}
/** * batadv_meshif_destroy_netlink() - deletion of batadv_mesh_interface via * netlink * @mesh_iface: the to-be-removed batman-adv interface * @head: list pointer
*/ staticvoid batadv_meshif_destroy_netlink(struct net_device *mesh_iface, struct list_head *head)
{ struct batadv_priv *bat_priv = netdev_priv(mesh_iface); struct batadv_hard_iface *hard_iface; struct batadv_meshif_vlan *vlan;
while (!list_empty(&mesh_iface->adj_list.lower)) {
hard_iface = netdev_adjacent_get_private(mesh_iface->adj_list.lower.next);
batadv_hardif_disable_interface(hard_iface);
}
/* destroy the "untagged" VLAN */
vlan = batadv_meshif_vlan_get(bat_priv, BATADV_NO_FLAGS); if (vlan) {
batadv_meshif_destroy_vlan(bat_priv, vlan);
batadv_meshif_vlan_put(vlan);
}
unregister_netdevice_queue(mesh_iface, head);
}
/** * batadv_meshif_is_valid() - Check whether device is a batadv mesh interface * @net_dev: device which should be checked * * Return: true when net_dev is a batman-adv interface, false otherwise
*/ bool batadv_meshif_is_valid(conststruct net_device *net_dev)
{ if (net_dev->netdev_ops->ndo_start_xmit == batadv_interface_tx) returntrue;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.