diff options
Diffstat (limited to 'drivers/staging/batman-adv/routing.c')
-rw-r--r-- | drivers/staging/batman-adv/routing.c | 563 |
1 files changed, 244 insertions, 319 deletions
diff --git a/drivers/staging/batman-adv/routing.c b/drivers/staging/batman-adv/routing.c index f8464cad30b..e0d093f5d52 100644 --- a/drivers/staging/batman-adv/routing.c +++ b/drivers/staging/batman-adv/routing.c @@ -36,15 +36,16 @@ DECLARE_WAIT_QUEUE_HEAD(thread_wait); -static atomic_t data_ready_cond; atomic_t exit_cond; + void slide_own_bcast_window(struct batman_if *batman_if) { HASHIT(hashit); struct orig_node *orig_node; TYPE_OF_WORD *word; + unsigned long flags; - spin_lock(&orig_hash_lock); + spin_lock_irqsave(&orig_hash_lock, flags); while (hash_iterate(orig_hash, &hashit)) { orig_node = hashit.bucket->data; @@ -55,7 +56,7 @@ void slide_own_bcast_window(struct batman_if *batman_if) bit_packet_count(word); } - spin_unlock(&orig_hash_lock); + spin_unlock_irqrestore(&orig_hash_lock, flags); } static void update_HNA(struct orig_node *orig_node, @@ -365,10 +366,9 @@ static char count_real_packets(struct ethhdr *ethhdr, } void receive_bat_packet(struct ethhdr *ethhdr, - struct batman_packet *batman_packet, - unsigned char *hna_buff, - int hna_buff_len, - struct batman_if *if_incoming) + struct batman_packet *batman_packet, + unsigned char *hna_buff, int hna_buff_len, + struct batman_if *if_incoming) { struct batman_if *batman_if; struct orig_node *orig_neigh_node, *orig_node; @@ -566,95 +566,118 @@ void receive_bat_packet(struct ethhdr *ethhdr, 0, hna_buff_len, if_incoming); } - -static int receive_raw_packet(struct socket *raw_sock, - unsigned char *packet_buff, int packet_buff_len) +int recv_bat_packet(struct sk_buff *skb, + struct batman_if *batman_if) { - struct kvec iov; - struct msghdr msg; + struct ethhdr *ethhdr; + unsigned long flags; - iov.iov_base = packet_buff; - iov.iov_len = packet_buff_len; + /* drop packet if it has not necessary minimum size */ + if (skb_headlen(skb) < sizeof(struct batman_packet)) + return NET_RX_DROP; - msg.msg_flags = MSG_DONTWAIT; /* non-blocking */ - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_control = NULL; + ethhdr = (struct ethhdr *)skb_mac_header(skb); - return kernel_recvmsg(raw_sock, &msg, &iov, 1, packet_buff_len, - MSG_DONTWAIT); -} - -static void recv_bat_packet(struct ethhdr *ethhdr, - unsigned char *packet_buff, - int result, - struct batman_if *batman_if) -{ /* packet with broadcast indication but unicast recipient */ if (!is_bcast(ethhdr->h_dest)) - return; + return NET_RX_DROP; /* packet with broadcast sender address */ if (is_bcast(ethhdr->h_source)) - return; - - /* drop packet if it has not at least one batman packet as payload */ - if (result < sizeof(struct ethhdr) + sizeof(struct batman_packet)) - return; - - spin_lock(&orig_hash_lock); + return NET_RX_DROP; + + spin_lock_irqsave(&orig_hash_lock, flags); + /* TODO: we use headlen instead of "length", because + * only this data is paged in. */ + /* TODO: is another skb_copy needed here? there will be + * written on the data, but nobody (?) should further use + * this data */ receive_aggr_bat_packet(ethhdr, - packet_buff + sizeof(struct ethhdr), - result - sizeof(struct ethhdr), + skb->data, + skb_headlen(skb), batman_if); - spin_unlock(&orig_hash_lock); + spin_unlock_irqrestore(&orig_hash_lock, flags); + + kfree_skb(skb); + return NET_RX_SUCCESS; } -static void recv_my_icmp_packet(struct ethhdr *ethhdr, - struct icmp_packet *icmp_packet, - unsigned char *packet_buff, - int result) +static int recv_my_icmp_packet(struct sk_buff *skb) { struct orig_node *orig_node; + struct icmp_packet *icmp_packet; + struct ethhdr *ethhdr; + struct sk_buff *skb_old; + struct batman_if *batman_if; + int ret; + unsigned long flags; + uint8_t dstaddr[ETH_ALEN]; + + icmp_packet = (struct icmp_packet *) skb->data; + ethhdr = (struct ethhdr *) skb_mac_header(skb); /* add data to device queue */ if (icmp_packet->msg_type != ECHO_REQUEST) { bat_device_receive_packet(icmp_packet); - return; + return NET_RX_DROP; } /* answer echo request (ping) */ /* get routing information */ - spin_lock(&orig_hash_lock); + spin_lock_irqsave(&orig_hash_lock, flags); orig_node = ((struct orig_node *)hash_find(orig_hash, icmp_packet->orig)); + ret = NET_RX_DROP; if ((orig_node != NULL) && (orig_node->batman_if != NULL) && (orig_node->router != NULL)) { + + /* don't lock while sending the packets ... we therefore + * copy the required data before sending */ + batman_if = orig_node->batman_if; + memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + spin_unlock_irqrestore(&orig_hash_lock, flags); + + /* create a copy of the skb, if needed, to modify it. */ + skb_old = NULL; + if (!skb_clone_writable(skb, sizeof(struct icmp_packet))) { + skb_old = skb; + skb = skb_copy(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; + icmp_packet = (struct icmp_packet *) skb->data; + kfree_skb(skb_old); + } + memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); memcpy(icmp_packet->orig, ethhdr->h_dest, ETH_ALEN); icmp_packet->msg_type = ECHO_REPLY; icmp_packet->ttl = TTL; - send_raw_packet(packet_buff + sizeof(struct ethhdr), - result - sizeof(struct ethhdr), - orig_node->batman_if, - orig_node->router->addr); - } + send_skb_packet(skb, batman_if, dstaddr); + ret = NET_RX_SUCCESS; - spin_unlock(&orig_hash_lock); - return; + } else + spin_unlock_irqrestore(&orig_hash_lock, flags); + + return ret; } -static void recv_icmp_ttl_exceeded(struct icmp_packet *icmp_packet, - struct ethhdr *ethhdr, - unsigned char *packet_buff, - int result, - struct batman_if *batman_if) +static int recv_icmp_ttl_exceeded(struct sk_buff *skb) { unsigned char src_str[ETH_STR_LEN], dst_str[ETH_STR_LEN]; struct orig_node *orig_node; + struct icmp_packet *icmp_packet; + struct ethhdr *ethhdr; + struct sk_buff *skb_old; + struct batman_if *batman_if; + int ret; + unsigned long flags; + uint8_t dstaddr[ETH_ALEN]; + + icmp_packet = (struct icmp_packet *) skb->data; + ethhdr = (struct ethhdr *) skb_mac_header(skb); addr_to_string(src_str, icmp_packet->orig); addr_to_string(dst_str, icmp_packet->dst); @@ -663,74 +686,93 @@ static void recv_icmp_ttl_exceeded(struct icmp_packet *icmp_packet, /* send TTL exceeded if packet is an echo request (traceroute) */ if (icmp_packet->msg_type != ECHO_REQUEST) - return; + return NET_RX_DROP; /* get routing information */ - spin_lock(&orig_hash_lock); + spin_lock_irqsave(&orig_hash_lock, flags); orig_node = ((struct orig_node *) hash_find(orig_hash, icmp_packet->orig)); + ret = NET_RX_DROP; if ((orig_node != NULL) && (orig_node->batman_if != NULL) && (orig_node->router != NULL)) { + + /* don't lock while sending the packets ... we therefore + * copy the required data before sending */ + batman_if = orig_node->batman_if; + memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + spin_unlock_irqrestore(&orig_hash_lock, flags); + + /* create a copy of the skb, if needed, to modify it. */ + if (!skb_clone_writable(skb, sizeof(struct icmp_packet))) { + skb_old = skb; + skb = skb_copy(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; + icmp_packet = (struct icmp_packet *) skb->data; + kfree_skb(skb_old); + } + memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); memcpy(icmp_packet->orig, ethhdr->h_dest, ETH_ALEN); icmp_packet->msg_type = TTL_EXCEEDED; icmp_packet->ttl = TTL; - send_raw_packet(packet_buff + sizeof(struct ethhdr), - result - sizeof(struct ethhdr), - orig_node->batman_if, - orig_node->router->addr); + send_skb_packet(skb, batman_if, dstaddr); + ret = NET_RX_SUCCESS; - } + } else + spin_unlock_irqrestore(&orig_hash_lock, flags); - spin_unlock(&orig_hash_lock); + return ret; } - -static void recv_icmp_packet(struct ethhdr *ethhdr, - unsigned char *packet_buff, - int result, - struct batman_if *batman_if) +int recv_icmp_packet(struct sk_buff *skb) { struct icmp_packet *icmp_packet; + struct ethhdr *ethhdr; struct orig_node *orig_node; + struct sk_buff *skb_old; + struct batman_if *batman_if; + int hdr_size = sizeof(struct icmp_packet); + int ret; + unsigned long flags; + uint8_t dstaddr[ETH_ALEN]; + + /* drop packet if it has not necessary minimum size */ + if (skb_headlen(skb) < hdr_size) + return NET_RX_DROP; + + ethhdr = (struct ethhdr *)skb_mac_header(skb); /* packet with unicast indication but broadcast recipient */ if (is_bcast(ethhdr->h_dest)) - return; + return NET_RX_DROP; /* packet with broadcast sender address */ if (is_bcast(ethhdr->h_source)) - return; + return NET_RX_DROP; /* not for me */ if (!is_my_mac(ethhdr->h_dest)) - return; + return NET_RX_DROP; - /* drop packet if it has not necessary minimum size */ - if (result < sizeof(struct ethhdr) + sizeof(struct icmp_packet)) - return; - - icmp_packet = (struct icmp_packet *) - (packet_buff + sizeof(struct ethhdr)); + icmp_packet = (struct icmp_packet *) skb->data; /* packet for me */ if (is_my_mac(icmp_packet->dst)) - recv_my_icmp_packet(ethhdr, icmp_packet, packet_buff, result); + return recv_my_icmp_packet(skb); /* TTL exceeded */ - if (icmp_packet->ttl < 2) { - recv_icmp_ttl_exceeded(icmp_packet, ethhdr, packet_buff, result, - batman_if); - return; + if (icmp_packet->ttl < 2) + return recv_icmp_ttl_exceeded(skb); - } + ret = NET_RX_DROP; /* get routing information */ - spin_lock(&orig_hash_lock); + spin_lock_irqsave(&orig_hash_lock, flags); orig_node = ((struct orig_node *) hash_find(orig_hash, icmp_packet->dst)); @@ -738,133 +780,169 @@ static void recv_icmp_packet(struct ethhdr *ethhdr, (orig_node->batman_if != NULL) && (orig_node->router != NULL)) { + /* don't lock while sending the packets ... we therefore + * copy the required data before sending */ + batman_if = orig_node->batman_if; + memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + spin_unlock_irqrestore(&orig_hash_lock, flags); + + /* create a copy of the skb, if needed, to modify it. */ + if (!skb_clone_writable(skb, sizeof(struct icmp_packet))) { + skb_old = skb; + skb = skb_copy(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; + icmp_packet = (struct icmp_packet *) skb->data; + kfree_skb(skb_old); + } + /* decrement ttl */ icmp_packet->ttl--; /* route it */ - send_raw_packet(packet_buff + sizeof(struct ethhdr), - result - sizeof(struct ethhdr), - orig_node->batman_if, - orig_node->router->addr); - } - spin_unlock(&orig_hash_lock); + send_skb_packet(skb, batman_if, dstaddr); + ret = NET_RX_SUCCESS; + + } else + spin_unlock_irqrestore(&orig_hash_lock, flags); + + return ret; } -static void recv_unicast_packet(struct ethhdr *ethhdr, - unsigned char *packet_buff, - int result, - struct batman_if *batman_if) +int recv_unicast_packet(struct sk_buff *skb) { struct unicast_packet *unicast_packet; unsigned char src_str[ETH_STR_LEN], dst_str[ETH_STR_LEN]; struct orig_node *orig_node; - int hdr_size = sizeof(struct ethhdr) + sizeof(struct unicast_packet); + struct ethhdr *ethhdr; + struct batman_if *batman_if; + struct sk_buff *skb_old; + uint8_t dstaddr[ETH_ALEN]; + int hdr_size = sizeof(struct unicast_packet); + int ret; + unsigned long flags; + + /* drop packet if it has not necessary minimum size */ + if (skb_headlen(skb) < hdr_size) + return NET_RX_DROP; + + ethhdr = (struct ethhdr *) skb_mac_header(skb); /* packet with unicast indication but broadcast recipient */ if (is_bcast(ethhdr->h_dest)) - return; + return NET_RX_DROP; /* packet with broadcast sender address */ if (is_bcast(ethhdr->h_source)) - return; + return NET_RX_DROP; /* not for me */ if (!is_my_mac(ethhdr->h_dest)) - return; - - /* drop packet if it has not necessary minimum size */ - if (result < hdr_size) - return; + return NET_RX_DROP; - unicast_packet = (struct unicast_packet *) - (packet_buff + sizeof(struct ethhdr)); + unicast_packet = (struct unicast_packet *) skb->data; /* packet for me */ if (is_my_mac(unicast_packet->dest)) { - interface_rx(soft_device, packet_buff + hdr_size, - result - hdr_size); - return; - + interface_rx(skb, hdr_size); + return NET_RX_SUCCESS; } /* TTL exceeded */ if (unicast_packet->ttl < 2) { - addr_to_string(src_str, ((struct ethhdr *) - (unicast_packet + 1))->h_source); - addr_to_string(dst_str, unicast_packet->dest); + addr_to_string(src_str, ethhdr->h_source); + addr_to_string(dst_str, ethhdr->h_dest); printk(KERN_WARNING "batman-adv:Warning - can't send packet from %s to %s: ttl exceeded\n", src_str, dst_str); - return; + return NET_RX_DROP; } + ret = NET_RX_DROP; /* get routing information */ - spin_lock(&orig_hash_lock); + spin_lock_irqsave(&orig_hash_lock, flags); orig_node = ((struct orig_node *) hash_find(orig_hash, unicast_packet->dest)); if ((orig_node != NULL) && (orig_node->batman_if != NULL) && (orig_node->router != NULL)) { + + /* don't lock while sending the packets ... we therefore + * copy the required data before sending */ + batman_if = orig_node->batman_if; + memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + spin_unlock_irqrestore(&orig_hash_lock, flags); + + /* create a copy of the skb, if needed, to modify it. */ + if (!skb_clone_writable(skb, sizeof(struct unicast_packet))) { + skb_old = skb; + skb = skb_copy(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; + unicast_packet = (struct unicast_packet *) skb->data; + kfree_skb(skb_old); + } /* decrement ttl */ unicast_packet->ttl--; /* route it */ - send_raw_packet(packet_buff + sizeof(struct ethhdr), - result - sizeof(struct ethhdr), - orig_node->batman_if, - orig_node->router->addr); - } - spin_unlock(&orig_hash_lock); + send_skb_packet(skb, batman_if, dstaddr); + ret = NET_RX_SUCCESS; + + } else + spin_unlock_irqrestore(&orig_hash_lock, flags); + + return ret; } -static void recv_bcast_packet(struct ethhdr *ethhdr, - unsigned char *packet_buff, - int result, - struct batman_if *batman_if) +int recv_bcast_packet(struct sk_buff *skb) { struct orig_node *orig_node; struct bcast_packet *bcast_packet; - int hdr_size = sizeof(struct ethhdr) + sizeof(struct bcast_packet); + struct ethhdr *ethhdr; + int hdr_size = sizeof(struct bcast_packet); + unsigned long flags; + + /* drop packet if it has not necessary minimum size */ + if (skb_headlen(skb) < hdr_size) + return NET_RX_DROP; + + ethhdr = (struct ethhdr *)skb_mac_header(skb); /* packet with broadcast indication but unicast recipient */ if (!is_bcast(ethhdr->h_dest)) - return; + return NET_RX_DROP; /* packet with broadcast sender address */ if (is_bcast(ethhdr->h_source)) - return; - - /* drop packet if it has not necessary minimum size */ - if (result < hdr_size) - return; + return NET_RX_DROP; /* ignore broadcasts sent by myself */ if (is_my_mac(ethhdr->h_source)) - return; + return NET_RX_DROP; - bcast_packet = (struct bcast_packet *) - (packet_buff + sizeof(struct ethhdr)); + bcast_packet = (struct bcast_packet *) skb->data; /* ignore broadcasts originated by myself */ if (is_my_mac(bcast_packet->orig)) - return; + return NET_RX_DROP; - spin_lock(&orig_hash_lock); + spin_lock_irqsave(&orig_hash_lock, flags); orig_node = ((struct orig_node *) hash_find(orig_hash, bcast_packet->orig)); if (orig_node == NULL) { - spin_unlock(&orig_hash_lock); - return; + spin_unlock_irqrestore(&orig_hash_lock, flags); + return NET_RX_DROP; } /* check flood history */ if (get_bit_status(orig_node->bcast_bits, orig_node->last_bcast_seqno, ntohs(bcast_packet->seqno))) { - spin_unlock(&orig_hash_lock); - return; + spin_unlock_irqrestore(&orig_hash_lock, flags); + return NET_RX_DROP; } /* mark broadcast in flood history */ @@ -873,211 +951,58 @@ static void recv_bcast_packet(struct ethhdr *ethhdr, orig_node->last_bcast_seqno, 1)) orig_node->last_bcast_seqno = ntohs(bcast_packet->seqno); - spin_unlock(&orig_hash_lock); + spin_unlock_irqrestore(&orig_hash_lock, flags); + + /* rebroadcast packet */ + add_bcast_packet_to_list(skb); /* broadcast for me */ - interface_rx(soft_device, packet_buff + hdr_size, result - hdr_size); + interface_rx(skb, hdr_size); - /* rebroadcast packet */ - add_bcast_packet_to_list(packet_buff + sizeof(struct ethhdr), - result - sizeof(struct ethhdr)); + return NET_RX_SUCCESS; } -static void recv_vis_packet(struct ethhdr *ethhdr, - unsigned char *packet_buff, - int result) +int recv_vis_packet(struct sk_buff *skb) { struct vis_packet *vis_packet; - int hdr_size = sizeof(struct ethhdr) + sizeof(struct vis_packet); - int vis_info_len; + struct ethhdr *ethhdr; + int hdr_size = sizeof(struct vis_packet); + int ret; - /* drop if too short. */ - if (result < hdr_size) - return; + if (skb_headlen(skb) < hdr_size) + return NET_RX_DROP; + + vis_packet = (struct vis_packet *) skb->data; + ethhdr = (struct ethhdr *)skb_mac_header(skb); /* not for me */ if (!is_my_mac(ethhdr->h_dest)) - return; - - vis_packet = (struct vis_packet *)(packet_buff + sizeof(struct ethhdr)); - vis_info_len = result - hdr_size; + return NET_RX_DROP; /* ignore own packets */ if (is_my_mac(vis_packet->vis_orig)) - return; + return NET_RX_DROP; if (is_my_mac(vis_packet->sender_orig)) - return; + return NET_RX_DROP; switch (vis_packet->vis_type) { case VIS_TYPE_SERVER_SYNC: - receive_server_sync_packet(vis_packet, vis_info_len); + /* TODO: handle fragmented skbs properly */ + receive_server_sync_packet(vis_packet, skb_headlen(skb)); + ret = NET_RX_SUCCESS; break; case VIS_TYPE_CLIENT_UPDATE: - receive_client_update_packet(vis_packet, vis_info_len); + /* TODO: handle fragmented skbs properly */ + receive_client_update_packet(vis_packet, skb_headlen(skb)); + ret = NET_RX_SUCCESS; break; default: /* ignore unknown packet */ + ret = NET_RX_DROP; break; } -} - -static int recv_one_packet(struct batman_if *batman_if, - unsigned char *packet_buff) -{ - int result; - struct ethhdr *ethhdr; - struct batman_packet *batman_packet; - - result = receive_raw_packet(batman_if->raw_sock, packet_buff, - PACKBUFF_SIZE); - if (result <= 0) - return result; - - if (result < sizeof(struct ethhdr) + 2) - return 0; - - ethhdr = (struct ethhdr *)packet_buff; - batman_packet = (struct batman_packet *) - (packet_buff + sizeof(struct ethhdr)); - - if (batman_packet->version != COMPAT_VERSION) { - bat_dbg(DBG_BATMAN, - "Drop packet: incompatible batman version (%i)\n", - batman_packet->version); - return 0; - } - - switch (batman_packet->packet_type) { - /* batman originator packet */ - case BAT_PACKET: - recv_bat_packet(ethhdr, packet_buff, result, batman_if); - break; - - /* batman icmp packet */ - case BAT_ICMP: - recv_icmp_packet(ethhdr, packet_buff, result, batman_if); - break; - - /* unicast packet */ - case BAT_UNICAST: - recv_unicast_packet(ethhdr, packet_buff, result, batman_if); - break; - - /* broadcast packet */ - case BAT_BCAST: - recv_bcast_packet(ethhdr, - packet_buff, result, batman_if); - break; - - /* vis packet */ - case BAT_VIS: - recv_vis_packet(ethhdr, packet_buff, result); - break; - } - return 0; -} - - -static int discard_one_packet(struct batman_if *batman_if, - unsigned char *packet_buff) -{ - int result = -EAGAIN; - - if (batman_if->raw_sock) { - result = receive_raw_packet(batman_if->raw_sock, - packet_buff, - PACKBUFF_SIZE); - } - return result; -} - - -static bool is_interface_active(struct batman_if *batman_if) -{ - if (batman_if->if_active != IF_ACTIVE) - return false; - - return true; -} - -static void service_interface(struct batman_if *batman_if, - unsigned char *packet_buff) - -{ - int result; - - do { - if (is_interface_active(batman_if)) - result = recv_one_packet(batman_if, packet_buff); - else - result = discard_one_packet(batman_if, packet_buff); - } while (result >= 0); - - /* we perform none blocking reads, so EAGAIN indicates there - are no more packets to read. Anything else is a real - error.*/ - - if ((result < 0) && (result != -EAGAIN)) - printk(KERN_ERR "batman-adv:Could not receive packet from interface %s: %i\n", batman_if->dev, result); -} - -static void service_interfaces(unsigned char *packet_buffer) -{ - struct batman_if *batman_if; - rcu_read_lock(); - list_for_each_entry_rcu(batman_if, &if_list, list) { - rcu_read_unlock(); - service_interface(batman_if, packet_buffer); - rcu_read_lock(); - } - rcu_read_unlock(); -} - - -int packet_recv_thread(void *data) -{ - unsigned char *packet_buff; - - atomic_set(&data_ready_cond, 0); - atomic_set(&exit_cond, 0); - packet_buff = kmalloc(PACKBUFF_SIZE, GFP_KERNEL); - if (!packet_buff) { - printk(KERN_ERR"batman-adv:Could allocate memory for the packet buffer. :(\n"); - return -1; - } - - while ((!kthread_should_stop()) && (!atomic_read(&exit_cond))) { - - wait_event_interruptible(thread_wait, - (atomic_read(&data_ready_cond) || - atomic_read(&exit_cond))); - - atomic_set(&data_ready_cond, 0); - - if (kthread_should_stop() || atomic_read(&exit_cond)) - break; - - service_interfaces(packet_buff); - } - kfree(packet_buff); - - /* do not exit until kthread_stop() is actually called, - * otherwise it will wait for us forever. */ - while (!kthread_should_stop()) - schedule(); - - return 0; -} - -void batman_data_ready(struct sock *sk, int len) -{ - void (*data_ready)(struct sock *, int) = sk->sk_user_data; - - data_ready(sk, len); - - atomic_set(&data_ready_cond, 1); - wake_up_interruptible(&thread_wait); + return ret; } |