diff options
author | Antonio Quartulli <antonio@open-mesh.com> | 2013-11-05 13:31:08 -0500 |
---|---|---|
committer | Antonio Quartulli <antonio@meshcoding.com> | 2014-01-08 14:49:42 -0500 |
commit | 6c413b1c22a2c4ef324f1c6f2c282f1ca10a93b9 (patch) | |
tree | 5e7d10d691a64b8ae180d4f718a41120118c6432 /net/batman-adv/gateway_client.c | |
parent | 36484f84d567f79fc7cc62c4391c7752a0ede7f2 (diff) |
batman-adv: send every DHCP packet as bat-unicast
In different situations it is possible that the DHCP server
or client uses broadcast Ethernet frames to send messages
to each other. The GW component in batman-adv takes care of
using bat-unicast packets to bring broadcast DHCP
Discover/Requests to the "best" server.
On the way back the DHCP server usually sends unicasts,
but upon client request it may decide to use broadcasts as
well.
This patch improves the GW component so that it now snoops
and sends as unicast all the DHCP packets, no matter if they
were generated by a DHCP server or client.
Signed-off-by: Antonio Quartulli <antonio@open-mesh.com>
Signed-off-by: Marek Lindner <mareklindner@neomailbox.ch>
Diffstat (limited to 'net/batman-adv/gateway_client.c')
-rw-r--r-- | net/batman-adv/gateway_client.c | 171 |
1 files changed, 76 insertions, 95 deletions
diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index a5602ef0f262..4150a641c52e 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c | |||
@@ -28,11 +28,17 @@ | |||
28 | #include <linux/udp.h> | 28 | #include <linux/udp.h> |
29 | #include <linux/if_vlan.h> | 29 | #include <linux/if_vlan.h> |
30 | 30 | ||
31 | /* This is the offset of the options field in a dhcp packet starting at | 31 | /* These are the offsets of the "hw type" and "hw address length" in the dhcp |
32 | * the beginning of the dhcp header | 32 | * packet starting at the beginning of the dhcp header |
33 | */ | 33 | */ |
34 | #define BATADV_DHCP_OPTIONS_OFFSET 240 | 34 | #define BATADV_DHCP_HTYPE_OFFSET 1 |
35 | #define BATADV_DHCP_REQUEST 3 | 35 | #define BATADV_DHCP_HLEN_OFFSET 2 |
36 | /* Value of htype representing Ethernet */ | ||
37 | #define BATADV_DHCP_HTYPE_ETHERNET 0x01 | ||
38 | /* This is the offset of the "chaddr" field in the dhcp packet starting at the | ||
39 | * beginning of the dhcp header | ||
40 | */ | ||
41 | #define BATADV_DHCP_CHADDR_OFFSET 28 | ||
36 | 42 | ||
37 | static void batadv_gw_node_free_ref(struct batadv_gw_node *gw_node) | 43 | static void batadv_gw_node_free_ref(struct batadv_gw_node *gw_node) |
38 | { | 44 | { |
@@ -596,80 +602,39 @@ out: | |||
596 | return 0; | 602 | return 0; |
597 | } | 603 | } |
598 | 604 | ||
599 | /* this call might reallocate skb data */ | 605 | /** |
600 | static bool batadv_is_type_dhcprequest(struct sk_buff *skb, int header_len) | 606 | * batadv_gw_dhcp_recipient_get - check if a packet is a DHCP message |
601 | { | 607 | * @skb: the packet to check |
602 | int ret = false; | 608 | * @header_len: a pointer to the batman-adv header size |
603 | unsigned char *p; | 609 | * @chaddr: buffer where the client address will be stored. Valid |
604 | int pkt_len; | 610 | * only if the function returns BATADV_DHCP_TO_CLIENT |
605 | 611 | * | |
606 | if (skb_linearize(skb) < 0) | 612 | * Returns: |
607 | goto out; | 613 | * - BATADV_DHCP_NO if the packet is not a dhcp message or if there was an error |
608 | 614 | * while parsing it | |
609 | pkt_len = skb_headlen(skb); | 615 | * - BATADV_DHCP_TO_SERVER if this is a message going to the DHCP server |
610 | 616 | * - BATADV_DHCP_TO_CLIENT if this is a message going to a DHCP client | |
611 | if (pkt_len < header_len + BATADV_DHCP_OPTIONS_OFFSET + 1) | 617 | * |
612 | goto out; | 618 | * This function may re-allocate the data buffer of the skb passed as argument. |
613 | 619 | */ | |
614 | p = skb->data + header_len + BATADV_DHCP_OPTIONS_OFFSET; | 620 | enum batadv_dhcp_recipient |
615 | pkt_len -= header_len + BATADV_DHCP_OPTIONS_OFFSET + 1; | 621 | batadv_gw_dhcp_recipient_get(struct sk_buff *skb, unsigned int *header_len, |
616 | 622 | uint8_t *chaddr) | |
617 | /* Access the dhcp option lists. Each entry is made up by: | ||
618 | * - octet 1: option type | ||
619 | * - octet 2: option data len (only if type != 255 and 0) | ||
620 | * - octet 3: option data | ||
621 | */ | ||
622 | while (*p != 255 && !ret) { | ||
623 | /* p now points to the first octet: option type */ | ||
624 | if (*p == 53) { | ||
625 | /* type 53 is the message type option. | ||
626 | * Jump the len octet and go to the data octet | ||
627 | */ | ||
628 | if (pkt_len < 2) | ||
629 | goto out; | ||
630 | p += 2; | ||
631 | |||
632 | /* check if the message type is what we need */ | ||
633 | if (*p == BATADV_DHCP_REQUEST) | ||
634 | ret = true; | ||
635 | break; | ||
636 | } else if (*p == 0) { | ||
637 | /* option type 0 (padding), just go forward */ | ||
638 | if (pkt_len < 1) | ||
639 | goto out; | ||
640 | pkt_len--; | ||
641 | p++; | ||
642 | } else { | ||
643 | /* This is any other option. So we get the length... */ | ||
644 | if (pkt_len < 1) | ||
645 | goto out; | ||
646 | pkt_len--; | ||
647 | p++; | ||
648 | |||
649 | /* ...and then we jump over the data */ | ||
650 | if (pkt_len < 1 + (*p)) | ||
651 | goto out; | ||
652 | pkt_len -= 1 + (*p); | ||
653 | p += 1 + (*p); | ||
654 | } | ||
655 | } | ||
656 | out: | ||
657 | return ret; | ||
658 | } | ||
659 | |||
660 | /* this call might reallocate skb data */ | ||
661 | bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len) | ||
662 | { | 623 | { |
624 | enum batadv_dhcp_recipient ret = BATADV_DHCP_NO; | ||
663 | struct ethhdr *ethhdr; | 625 | struct ethhdr *ethhdr; |
664 | struct iphdr *iphdr; | 626 | struct iphdr *iphdr; |
665 | struct ipv6hdr *ipv6hdr; | 627 | struct ipv6hdr *ipv6hdr; |
666 | struct udphdr *udphdr; | 628 | struct udphdr *udphdr; |
667 | struct vlan_ethhdr *vhdr; | 629 | struct vlan_ethhdr *vhdr; |
630 | int chaddr_offset; | ||
668 | __be16 proto; | 631 | __be16 proto; |
632 | uint8_t *p; | ||
669 | 633 | ||
670 | /* check for ethernet header */ | 634 | /* check for ethernet header */ |
671 | if (!pskb_may_pull(skb, *header_len + ETH_HLEN)) | 635 | if (!pskb_may_pull(skb, *header_len + ETH_HLEN)) |
672 | return false; | 636 | return BATADV_DHCP_NO; |
637 | |||
673 | ethhdr = (struct ethhdr *)skb->data; | 638 | ethhdr = (struct ethhdr *)skb->data; |
674 | proto = ethhdr->h_proto; | 639 | proto = ethhdr->h_proto; |
675 | *header_len += ETH_HLEN; | 640 | *header_len += ETH_HLEN; |
@@ -677,7 +642,7 @@ bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len) | |||
677 | /* check for initial vlan header */ | 642 | /* check for initial vlan header */ |
678 | if (proto == htons(ETH_P_8021Q)) { | 643 | if (proto == htons(ETH_P_8021Q)) { |
679 | if (!pskb_may_pull(skb, *header_len + VLAN_HLEN)) | 644 | if (!pskb_may_pull(skb, *header_len + VLAN_HLEN)) |
680 | return false; | 645 | return BATADV_DHCP_NO; |
681 | 646 | ||
682 | vhdr = (struct vlan_ethhdr *)skb->data; | 647 | vhdr = (struct vlan_ethhdr *)skb->data; |
683 | proto = vhdr->h_vlan_encapsulated_proto; | 648 | proto = vhdr->h_vlan_encapsulated_proto; |
@@ -688,32 +653,34 @@ bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len) | |||
688 | switch (proto) { | 653 | switch (proto) { |
689 | case htons(ETH_P_IP): | 654 | case htons(ETH_P_IP): |
690 | if (!pskb_may_pull(skb, *header_len + sizeof(*iphdr))) | 655 | if (!pskb_may_pull(skb, *header_len + sizeof(*iphdr))) |
691 | return false; | 656 | return BATADV_DHCP_NO; |
657 | |||
692 | iphdr = (struct iphdr *)(skb->data + *header_len); | 658 | iphdr = (struct iphdr *)(skb->data + *header_len); |
693 | *header_len += iphdr->ihl * 4; | 659 | *header_len += iphdr->ihl * 4; |
694 | 660 | ||
695 | /* check for udp header */ | 661 | /* check for udp header */ |
696 | if (iphdr->protocol != IPPROTO_UDP) | 662 | if (iphdr->protocol != IPPROTO_UDP) |
697 | return false; | 663 | return BATADV_DHCP_NO; |
698 | 664 | ||
699 | break; | 665 | break; |
700 | case htons(ETH_P_IPV6): | 666 | case htons(ETH_P_IPV6): |
701 | if (!pskb_may_pull(skb, *header_len + sizeof(*ipv6hdr))) | 667 | if (!pskb_may_pull(skb, *header_len + sizeof(*ipv6hdr))) |
702 | return false; | 668 | return BATADV_DHCP_NO; |
669 | |||
703 | ipv6hdr = (struct ipv6hdr *)(skb->data + *header_len); | 670 | ipv6hdr = (struct ipv6hdr *)(skb->data + *header_len); |
704 | *header_len += sizeof(*ipv6hdr); | 671 | *header_len += sizeof(*ipv6hdr); |
705 | 672 | ||
706 | /* check for udp header */ | 673 | /* check for udp header */ |
707 | if (ipv6hdr->nexthdr != IPPROTO_UDP) | 674 | if (ipv6hdr->nexthdr != IPPROTO_UDP) |
708 | return false; | 675 | return BATADV_DHCP_NO; |
709 | 676 | ||
710 | break; | 677 | break; |
711 | default: | 678 | default: |
712 | return false; | 679 | return BATADV_DHCP_NO; |
713 | } | 680 | } |
714 | 681 | ||
715 | if (!pskb_may_pull(skb, *header_len + sizeof(*udphdr))) | 682 | if (!pskb_may_pull(skb, *header_len + sizeof(*udphdr))) |
716 | return false; | 683 | return BATADV_DHCP_NO; |
717 | 684 | ||
718 | /* skb->data might have been reallocated by pskb_may_pull() */ | 685 | /* skb->data might have been reallocated by pskb_may_pull() */ |
719 | ethhdr = (struct ethhdr *)skb->data; | 686 | ethhdr = (struct ethhdr *)skb->data; |
@@ -724,17 +691,40 @@ bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len) | |||
724 | *header_len += sizeof(*udphdr); | 691 | *header_len += sizeof(*udphdr); |
725 | 692 | ||
726 | /* check for bootp port */ | 693 | /* check for bootp port */ |
727 | if ((proto == htons(ETH_P_IP)) && | 694 | switch (proto) { |
728 | (udphdr->dest != htons(67))) | 695 | case htons(ETH_P_IP): |
729 | return false; | 696 | if (udphdr->dest == htons(67)) |
697 | ret = BATADV_DHCP_TO_SERVER; | ||
698 | else if (udphdr->source == htons(67)) | ||
699 | ret = BATADV_DHCP_TO_CLIENT; | ||
700 | break; | ||
701 | case htons(ETH_P_IPV6): | ||
702 | if (udphdr->dest == htons(547)) | ||
703 | ret = BATADV_DHCP_TO_SERVER; | ||
704 | else if (udphdr->source == htons(547)) | ||
705 | ret = BATADV_DHCP_TO_CLIENT; | ||
706 | break; | ||
707 | } | ||
730 | 708 | ||
731 | if ((proto == htons(ETH_P_IPV6)) && | 709 | chaddr_offset = *header_len + BATADV_DHCP_CHADDR_OFFSET; |
732 | (udphdr->dest != htons(547))) | 710 | /* store the client address if the message is going to a client */ |
733 | return false; | 711 | if (ret == BATADV_DHCP_TO_CLIENT && |
712 | pskb_may_pull(skb, chaddr_offset + ETH_ALEN)) { | ||
713 | /* check if the DHCP packet carries an Ethernet DHCP */ | ||
714 | p = skb->data + *header_len + BATADV_DHCP_HTYPE_OFFSET; | ||
715 | if (*p != BATADV_DHCP_HTYPE_ETHERNET) | ||
716 | return BATADV_DHCP_NO; | ||
717 | |||
718 | /* check if the DHCP packet carries a valid Ethernet address */ | ||
719 | p = skb->data + *header_len + BATADV_DHCP_HLEN_OFFSET; | ||
720 | if (*p != ETH_ALEN) | ||
721 | return BATADV_DHCP_NO; | ||
722 | |||
723 | memcpy(chaddr, skb->data + chaddr_offset, ETH_ALEN); | ||
724 | } | ||
734 | 725 | ||
735 | return true; | 726 | return ret; |
736 | } | 727 | } |
737 | |||
738 | /** | 728 | /** |
739 | * batadv_gw_out_of_range - check if the dhcp request destination is the best gw | 729 | * batadv_gw_out_of_range - check if the dhcp request destination is the best gw |
740 | * @bat_priv: the bat priv with all the soft interface information | 730 | * @bat_priv: the bat priv with all the soft interface information |
@@ -748,6 +738,7 @@ bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len) | |||
748 | * false otherwise. | 738 | * false otherwise. |
749 | * | 739 | * |
750 | * This call might reallocate skb data. | 740 | * This call might reallocate skb data. |
741 | * Must be invoked only when the DHCP packet is going TO a DHCP SERVER. | ||
751 | */ | 742 | */ |
752 | bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, | 743 | bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, |
753 | struct sk_buff *skb) | 744 | struct sk_buff *skb) |
@@ -755,19 +746,13 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, | |||
755 | struct batadv_neigh_node *neigh_curr = NULL, *neigh_old = NULL; | 746 | struct batadv_neigh_node *neigh_curr = NULL, *neigh_old = NULL; |
756 | struct batadv_orig_node *orig_dst_node = NULL; | 747 | struct batadv_orig_node *orig_dst_node = NULL; |
757 | struct batadv_gw_node *gw_node = NULL, *curr_gw = NULL; | 748 | struct batadv_gw_node *gw_node = NULL, *curr_gw = NULL; |
758 | struct ethhdr *ethhdr; | 749 | struct ethhdr *ethhdr = (struct ethhdr *)skb->data; |
759 | bool ret, out_of_range = false; | 750 | bool out_of_range = false; |
760 | unsigned int header_len = 0; | ||
761 | uint8_t curr_tq_avg; | 751 | uint8_t curr_tq_avg; |
762 | unsigned short vid; | 752 | unsigned short vid; |
763 | 753 | ||
764 | vid = batadv_get_vid(skb, 0); | 754 | vid = batadv_get_vid(skb, 0); |
765 | 755 | ||
766 | ret = batadv_gw_is_dhcp_target(skb, &header_len); | ||
767 | if (!ret) | ||
768 | goto out; | ||
769 | |||
770 | ethhdr = (struct ethhdr *)skb->data; | ||
771 | orig_dst_node = batadv_transtable_search(bat_priv, ethhdr->h_source, | 756 | orig_dst_node = batadv_transtable_search(bat_priv, ethhdr->h_source, |
772 | ethhdr->h_dest, vid); | 757 | ethhdr->h_dest, vid); |
773 | if (!orig_dst_node) | 758 | if (!orig_dst_node) |
@@ -777,10 +762,6 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, | |||
777 | if (!gw_node->bandwidth_down == 0) | 762 | if (!gw_node->bandwidth_down == 0) |
778 | goto out; | 763 | goto out; |
779 | 764 | ||
780 | ret = batadv_is_type_dhcprequest(skb, header_len); | ||
781 | if (!ret) | ||
782 | goto out; | ||
783 | |||
784 | switch (atomic_read(&bat_priv->gw_mode)) { | 765 | switch (atomic_read(&bat_priv->gw_mode)) { |
785 | case BATADV_GW_MODE_SERVER: | 766 | case BATADV_GW_MODE_SERVER: |
786 | /* If we are a GW then we are our best GW. We can artificially | 767 | /* If we are a GW then we are our best GW. We can artificially |