diff options
-rw-r--r-- | net/batman-adv/gateway_client.c | 153 | ||||
-rw-r--r-- | net/batman-adv/gateway_client.h | 5 | ||||
-rw-r--r-- | net/batman-adv/soft-interface.c | 43 |
3 files changed, 126 insertions, 75 deletions
diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index 619fb73b3b76..9373a143c6d4 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include "gateway_common.h" | 25 | #include "gateway_common.h" |
26 | #include "hard-interface.h" | 26 | #include "hard-interface.h" |
27 | #include "originator.h" | 27 | #include "originator.h" |
28 | #include "translation-table.h" | ||
28 | #include "routing.h" | 29 | #include "routing.h" |
29 | #include <linux/ip.h> | 30 | #include <linux/ip.h> |
30 | #include <linux/ipv6.h> | 31 | #include <linux/ipv6.h> |
@@ -572,108 +573,142 @@ out: | |||
572 | return ret; | 573 | return ret; |
573 | } | 574 | } |
574 | 575 | ||
575 | int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb, | 576 | bool gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len) |
576 | struct orig_node *old_gw) | ||
577 | { | 577 | { |
578 | struct ethhdr *ethhdr; | 578 | struct ethhdr *ethhdr; |
579 | struct iphdr *iphdr; | 579 | struct iphdr *iphdr; |
580 | struct ipv6hdr *ipv6hdr; | 580 | struct ipv6hdr *ipv6hdr; |
581 | struct udphdr *udphdr; | 581 | struct udphdr *udphdr; |
582 | struct gw_node *curr_gw; | ||
583 | struct neigh_node *neigh_curr = NULL, *neigh_old = NULL; | ||
584 | unsigned int header_len = 0; | ||
585 | int ret = 1; | ||
586 | |||
587 | if (atomic_read(&bat_priv->gw_mode) == GW_MODE_OFF) | ||
588 | return 0; | ||
589 | 582 | ||
590 | /* check for ethernet header */ | 583 | /* check for ethernet header */ |
591 | if (!pskb_may_pull(skb, header_len + ETH_HLEN)) | 584 | if (!pskb_may_pull(skb, *header_len + ETH_HLEN)) |
592 | return 0; | 585 | return false; |
593 | ethhdr = (struct ethhdr *)skb->data; | 586 | ethhdr = (struct ethhdr *)skb->data; |
594 | header_len += ETH_HLEN; | 587 | *header_len += ETH_HLEN; |
595 | 588 | ||
596 | /* check for initial vlan header */ | 589 | /* check for initial vlan header */ |
597 | if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) { | 590 | if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) { |
598 | if (!pskb_may_pull(skb, header_len + VLAN_HLEN)) | 591 | if (!pskb_may_pull(skb, *header_len + VLAN_HLEN)) |
599 | return 0; | 592 | return false; |
600 | ethhdr = (struct ethhdr *)(skb->data + VLAN_HLEN); | 593 | ethhdr = (struct ethhdr *)(skb->data + VLAN_HLEN); |
601 | header_len += VLAN_HLEN; | 594 | *header_len += VLAN_HLEN; |
602 | } | 595 | } |
603 | 596 | ||
604 | /* check for ip header */ | 597 | /* check for ip header */ |
605 | switch (ntohs(ethhdr->h_proto)) { | 598 | switch (ntohs(ethhdr->h_proto)) { |
606 | case ETH_P_IP: | 599 | case ETH_P_IP: |
607 | if (!pskb_may_pull(skb, header_len + sizeof(*iphdr))) | 600 | if (!pskb_may_pull(skb, *header_len + sizeof(*iphdr))) |
608 | return 0; | 601 | return false; |
609 | iphdr = (struct iphdr *)(skb->data + header_len); | 602 | iphdr = (struct iphdr *)(skb->data + *header_len); |
610 | header_len += iphdr->ihl * 4; | 603 | *header_len += iphdr->ihl * 4; |
611 | 604 | ||
612 | /* check for udp header */ | 605 | /* check for udp header */ |
613 | if (iphdr->protocol != IPPROTO_UDP) | 606 | if (iphdr->protocol != IPPROTO_UDP) |
614 | return 0; | 607 | return false; |
615 | 608 | ||
616 | break; | 609 | break; |
617 | case ETH_P_IPV6: | 610 | case ETH_P_IPV6: |
618 | if (!pskb_may_pull(skb, header_len + sizeof(*ipv6hdr))) | 611 | if (!pskb_may_pull(skb, *header_len + sizeof(*ipv6hdr))) |
619 | return 0; | 612 | return false; |
620 | ipv6hdr = (struct ipv6hdr *)(skb->data + header_len); | 613 | ipv6hdr = (struct ipv6hdr *)(skb->data + *header_len); |
621 | header_len += sizeof(*ipv6hdr); | 614 | *header_len += sizeof(*ipv6hdr); |
622 | 615 | ||
623 | /* check for udp header */ | 616 | /* check for udp header */ |
624 | if (ipv6hdr->nexthdr != IPPROTO_UDP) | 617 | if (ipv6hdr->nexthdr != IPPROTO_UDP) |
625 | return 0; | 618 | return false; |
626 | 619 | ||
627 | break; | 620 | break; |
628 | default: | 621 | default: |
629 | return 0; | 622 | return false; |
630 | } | 623 | } |
631 | 624 | ||
632 | if (!pskb_may_pull(skb, header_len + sizeof(*udphdr))) | 625 | if (!pskb_may_pull(skb, *header_len + sizeof(*udphdr))) |
633 | return 0; | 626 | return false; |
634 | udphdr = (struct udphdr *)(skb->data + header_len); | 627 | udphdr = (struct udphdr *)(skb->data + *header_len); |
635 | header_len += sizeof(*udphdr); | 628 | *header_len += sizeof(*udphdr); |
636 | 629 | ||
637 | /* check for bootp port */ | 630 | /* check for bootp port */ |
638 | if ((ntohs(ethhdr->h_proto) == ETH_P_IP) && | 631 | if ((ntohs(ethhdr->h_proto) == ETH_P_IP) && |
639 | (ntohs(udphdr->dest) != 67)) | 632 | (ntohs(udphdr->dest) != 67)) |
640 | return 0; | 633 | return false; |
641 | 634 | ||
642 | if ((ntohs(ethhdr->h_proto) == ETH_P_IPV6) && | 635 | if ((ntohs(ethhdr->h_proto) == ETH_P_IPV6) && |
643 | (ntohs(udphdr->dest) != 547)) | 636 | (ntohs(udphdr->dest) != 547)) |
644 | return 0; | 637 | return false; |
645 | 638 | ||
646 | if (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER) | 639 | return true; |
647 | return -1; | 640 | } |
648 | 641 | ||
649 | curr_gw = gw_get_selected_gw_node(bat_priv); | 642 | bool gw_out_of_range(struct bat_priv *bat_priv, |
650 | if (!curr_gw) | 643 | struct sk_buff *skb, struct ethhdr *ethhdr) |
651 | return 0; | 644 | { |
652 | 645 | struct neigh_node *neigh_curr = NULL, *neigh_old = NULL; | |
653 | /* If old_gw != NULL then this packet is unicast. | 646 | struct orig_node *orig_dst_node = NULL; |
654 | * So, at this point we have to check the message type: if it is a | 647 | struct gw_node *curr_gw = NULL; |
655 | * DHCPREQUEST we have to decide whether to drop it or not */ | 648 | bool ret, out_of_range = false; |
656 | if (old_gw && curr_gw->orig_node != old_gw) { | 649 | unsigned int header_len = 0; |
657 | if (is_type_dhcprequest(skb, header_len)) { | 650 | uint8_t curr_tq_avg; |
658 | /* If the dhcp packet has been sent to a different gw, | 651 | |
659 | * we have to evaluate whether the old gw is still | 652 | ret = gw_is_dhcp_target(skb, &header_len); |
660 | * reliable enough */ | 653 | if (!ret) |
661 | neigh_curr = find_router(bat_priv, curr_gw->orig_node, | 654 | goto out; |
662 | NULL); | 655 | |
663 | neigh_old = find_router(bat_priv, old_gw, NULL); | 656 | orig_dst_node = transtable_search(bat_priv, ethhdr->h_source, |
664 | if (!neigh_curr || !neigh_old) | 657 | ethhdr->h_dest); |
665 | goto free_neigh; | 658 | if (!orig_dst_node) |
666 | if (neigh_curr->tq_avg - neigh_old->tq_avg < | 659 | goto out; |
667 | GW_THRESHOLD) | 660 | |
668 | ret = -1; | 661 | if (!orig_dst_node->gw_flags) |
669 | } | 662 | goto out; |
663 | |||
664 | ret = is_type_dhcprequest(skb, header_len); | ||
665 | if (!ret) | ||
666 | goto out; | ||
667 | |||
668 | switch (atomic_read(&bat_priv->gw_mode)) { | ||
669 | case GW_MODE_SERVER: | ||
670 | /* If we are a GW then we are our best GW. We can artificially | ||
671 | * set the tq towards ourself as the maximum value */ | ||
672 | curr_tq_avg = TQ_MAX_VALUE; | ||
673 | break; | ||
674 | case GW_MODE_CLIENT: | ||
675 | curr_gw = gw_get_selected_gw_node(bat_priv); | ||
676 | if (!curr_gw) | ||
677 | goto out; | ||
678 | |||
679 | /* packet is going to our gateway */ | ||
680 | if (curr_gw->orig_node == orig_dst_node) | ||
681 | goto out; | ||
682 | |||
683 | /* If the dhcp packet has been sent to a different gw, | ||
684 | * we have to evaluate whether the old gw is still | ||
685 | * reliable enough */ | ||
686 | neigh_curr = find_router(bat_priv, curr_gw->orig_node, NULL); | ||
687 | if (!neigh_curr) | ||
688 | goto out; | ||
689 | |||
690 | curr_tq_avg = neigh_curr->tq_avg; | ||
691 | break; | ||
692 | case GW_MODE_OFF: | ||
693 | default: | ||
694 | goto out; | ||
670 | } | 695 | } |
671 | free_neigh: | 696 | |
697 | neigh_old = find_router(bat_priv, orig_dst_node, NULL); | ||
698 | if (!!neigh_old) | ||
699 | goto out; | ||
700 | |||
701 | if (curr_tq_avg - neigh_old->tq_avg > GW_THRESHOLD) | ||
702 | out_of_range = true; | ||
703 | |||
704 | out: | ||
705 | if (orig_dst_node) | ||
706 | orig_node_free_ref(orig_dst_node); | ||
707 | if (curr_gw) | ||
708 | gw_node_free_ref(curr_gw); | ||
672 | if (neigh_old) | 709 | if (neigh_old) |
673 | neigh_node_free_ref(neigh_old); | 710 | neigh_node_free_ref(neigh_old); |
674 | if (neigh_curr) | 711 | if (neigh_curr) |
675 | neigh_node_free_ref(neigh_curr); | 712 | neigh_node_free_ref(neigh_curr); |
676 | if (curr_gw) | 713 | return out_of_range; |
677 | gw_node_free_ref(curr_gw); | ||
678 | return ret; | ||
679 | } | 714 | } |
diff --git a/net/batman-adv/gateway_client.h b/net/batman-adv/gateway_client.h index b9b983c07feb..e1edba08eb1d 100644 --- a/net/batman-adv/gateway_client.h +++ b/net/batman-adv/gateway_client.h | |||
@@ -31,7 +31,8 @@ void gw_node_update(struct bat_priv *bat_priv, | |||
31 | void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node); | 31 | void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node); |
32 | void gw_node_purge(struct bat_priv *bat_priv); | 32 | void gw_node_purge(struct bat_priv *bat_priv); |
33 | int gw_client_seq_print_text(struct seq_file *seq, void *offset); | 33 | int gw_client_seq_print_text(struct seq_file *seq, void *offset); |
34 | int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb, | 34 | bool gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len); |
35 | struct orig_node *old_gw); | 35 | bool gw_out_of_range(struct bat_priv *bat_priv, |
36 | struct sk_buff *skb, struct ethhdr *ethhdr); | ||
36 | 37 | ||
37 | #endif /* _NET_BATMAN_ADV_GATEWAY_CLIENT_H_ */ | 38 | #endif /* _NET_BATMAN_ADV_GATEWAY_CLIENT_H_ */ |
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index f9cc95728989..45297c843092 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c | |||
@@ -563,10 +563,10 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) | |||
563 | struct bcast_packet *bcast_packet; | 563 | struct bcast_packet *bcast_packet; |
564 | struct vlan_ethhdr *vhdr; | 564 | struct vlan_ethhdr *vhdr; |
565 | struct softif_neigh *curr_softif_neigh = NULL; | 565 | struct softif_neigh *curr_softif_neigh = NULL; |
566 | struct orig_node *orig_node = NULL; | 566 | unsigned int header_len = 0; |
567 | int data_len = skb->len, ret; | 567 | int data_len = skb->len, ret; |
568 | short vid = -1; | 568 | short vid = -1; |
569 | bool do_bcast; | 569 | bool do_bcast = false; |
570 | 570 | ||
571 | if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE) | 571 | if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE) |
572 | goto dropped; | 572 | goto dropped; |
@@ -598,17 +598,28 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) | |||
598 | /* Register the client MAC in the transtable */ | 598 | /* Register the client MAC in the transtable */ |
599 | tt_local_add(soft_iface, ethhdr->h_source, skb->skb_iif); | 599 | tt_local_add(soft_iface, ethhdr->h_source, skb->skb_iif); |
600 | 600 | ||
601 | orig_node = transtable_search(bat_priv, ethhdr->h_source, | 601 | if (is_multicast_ether_addr(ethhdr->h_dest)) { |
602 | ethhdr->h_dest); | 602 | do_bcast = true; |
603 | do_bcast = is_multicast_ether_addr(ethhdr->h_dest); | ||
604 | if (do_bcast || (orig_node && orig_node->gw_flags)) { | ||
605 | ret = gw_is_target(bat_priv, skb, orig_node); | ||
606 | 603 | ||
607 | if (ret < 0) | 604 | switch (atomic_read(&bat_priv->gw_mode)) { |
608 | goto dropped; | 605 | case GW_MODE_SERVER: |
609 | 606 | /* gateway servers should not send dhcp | |
610 | if (ret) | 607 | * requests into the mesh */ |
611 | do_bcast = false; | 608 | ret = gw_is_dhcp_target(skb, &header_len); |
609 | if (ret) | ||
610 | goto dropped; | ||
611 | break; | ||
612 | case GW_MODE_CLIENT: | ||
613 | /* gateway clients should send dhcp requests | ||
614 | * via unicast to their gateway */ | ||
615 | ret = gw_is_dhcp_target(skb, &header_len); | ||
616 | if (ret) | ||
617 | do_bcast = false; | ||
618 | break; | ||
619 | case GW_MODE_OFF: | ||
620 | default: | ||
621 | break; | ||
622 | } | ||
612 | } | 623 | } |
613 | 624 | ||
614 | /* ethernet packet should be broadcasted */ | 625 | /* ethernet packet should be broadcasted */ |
@@ -644,6 +655,12 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) | |||
644 | 655 | ||
645 | /* unicast packet */ | 656 | /* unicast packet */ |
646 | } else { | 657 | } else { |
658 | if (atomic_read(&bat_priv->gw_mode) != GW_MODE_OFF) { | ||
659 | ret = gw_out_of_range(bat_priv, skb, ethhdr); | ||
660 | if (ret) | ||
661 | goto dropped; | ||
662 | } | ||
663 | |||
647 | ret = unicast_send_skb(skb, bat_priv); | 664 | ret = unicast_send_skb(skb, bat_priv); |
648 | if (ret != 0) | 665 | if (ret != 0) |
649 | goto dropped_freed; | 666 | goto dropped_freed; |
@@ -662,8 +679,6 @@ end: | |||
662 | softif_neigh_free_ref(curr_softif_neigh); | 679 | softif_neigh_free_ref(curr_softif_neigh); |
663 | if (primary_if) | 680 | if (primary_if) |
664 | hardif_free_ref(primary_if); | 681 | hardif_free_ref(primary_if); |
665 | if (orig_node) | ||
666 | orig_node_free_ref(orig_node); | ||
667 | return NETDEV_TX_OK; | 682 | return NETDEV_TX_OK; |
668 | } | 683 | } |
669 | 684 | ||