aboutsummaryrefslogtreecommitdiffstats
path: root/net/batman-adv/gateway_client.c
diff options
context:
space:
mode:
authorAntonio Quartulli <antonio@open-mesh.com>2013-11-05 13:31:08 -0500
committerAntonio Quartulli <antonio@meshcoding.com>2014-01-08 14:49:42 -0500
commit6c413b1c22a2c4ef324f1c6f2c282f1ca10a93b9 (patch)
tree5e7d10d691a64b8ae180d4f718a41120118c6432 /net/batman-adv/gateway_client.c
parent36484f84d567f79fc7cc62c4391c7752a0ede7f2 (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.c171
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
37static void batadv_gw_node_free_ref(struct batadv_gw_node *gw_node) 43static 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/**
600static 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; 620enum batadv_dhcp_recipient
615 pkt_len -= header_len + BATADV_DHCP_OPTIONS_OFFSET + 1; 621batadv_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 }
656out:
657 return ret;
658}
659
660/* this call might reallocate skb data */
661bool 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 */
752bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, 743bool 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