Commit b61ec31c85756bbc898fb892555509afe709459a

Authored by Linus Lüssing
Committed by Simon Wunderlich
1 parent 25c097ca41

batman-adv: Snoop DHCPACKs for DAT

In a 1000 nodes mesh network (Freifunk Hamburg) we can still see
30KBit/s of ARP traffic (equalling about 25% of all layer two
specific overhead, remaining after some filtering) flooded through
the mesh. These 30KBit/s are mainly ARP Requests from the
gateways / DHCP servers.

By snooping DHCPACKs we can learn about MAC/IP address pairs
in the DHCP range without relying on ARP. This patch is in preparation
to eliminate the need for mesh wide message flooding for IPv4 address
resolution.

Also this allows to quickly update a MAC/IP pair at least in the DHT when
DHCP reassigns an IP address to a new host.

Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>

Showing 4 changed files with 431 additions and 2 deletions Side-by-side Diff

net/batman-adv/distributed-arp-table.c
... ... @@ -19,6 +19,7 @@
19 19 #include "distributed-arp-table.h"
20 20 #include "main.h"
21 21  
  22 +#include <asm/unaligned.h>
22 23 #include <linux/atomic.h>
23 24 #include <linux/bitops.h>
24 25 #include <linux/byteorder/generic.h>
... ... @@ -29,6 +30,7 @@
29 30 #include <linux/if_ether.h>
30 31 #include <linux/if_vlan.h>
31 32 #include <linux/in.h>
  33 +#include <linux/ip.h>
32 34 #include <linux/jiffies.h>
33 35 #include <linux/kernel.h>
34 36 #include <linux/kref.h>
... ... @@ -42,6 +44,7 @@
42 44 #include <linux/spinlock.h>
43 45 #include <linux/stddef.h>
44 46 #include <linux/string.h>
  47 +#include <linux/udp.h>
45 48 #include <linux/workqueue.h>
46 49 #include <net/arp.h>
47 50 #include <net/genetlink.h>
... ... @@ -60,6 +63,49 @@
60 63 #include "translation-table.h"
61 64 #include "tvlv.h"
62 65  
  66 +enum batadv_bootpop {
  67 + BATADV_BOOTREPLY = 2,
  68 +};
  69 +
  70 +enum batadv_boothtype {
  71 + BATADV_HTYPE_ETHERNET = 1,
  72 +};
  73 +
  74 +enum batadv_dhcpoptioncode {
  75 + BATADV_DHCP_OPT_PAD = 0,
  76 + BATADV_DHCP_OPT_MSG_TYPE = 53,
  77 + BATADV_DHCP_OPT_END = 255,
  78 +};
  79 +
  80 +enum batadv_dhcptype {
  81 + BATADV_DHCPACK = 5,
  82 +};
  83 +
  84 +/* { 99, 130, 83, 99 } */
  85 +#define BATADV_DHCP_MAGIC 1669485411
  86 +
  87 +struct batadv_dhcp_packet {
  88 + __u8 op;
  89 + __u8 htype;
  90 + __u8 hlen;
  91 + __u8 hops;
  92 + __be32 xid;
  93 + __be16 secs;
  94 + __be16 flags;
  95 + __be32 ciaddr;
  96 + __be32 yiaddr;
  97 + __be32 siaddr;
  98 + __be32 giaddr;
  99 + __u8 chaddr[16];
  100 + __u8 sname[64];
  101 + __u8 file[128];
  102 + __be32 magic;
  103 + __u8 options[0];
  104 +};
  105 +
  106 +#define BATADV_DHCP_YIADDR_LEN sizeof(((struct batadv_dhcp_packet *)0)->yiaddr)
  107 +#define BATADV_DHCP_CHADDR_LEN sizeof(((struct batadv_dhcp_packet *)0)->chaddr)
  108 +
63 109 static void batadv_dat_purge(struct work_struct *work);
64 110  
65 111 /**
... ... @@ -1437,6 +1483,359 @@
1437 1483 batadv_dat_entry_put(dat_entry);
1438 1484 /* if dropped == false -> deliver to the interface */
1439 1485 return dropped;
  1486 +}
  1487 +
  1488 +/**
  1489 + * batadv_dat_check_dhcp_ipudp() - check skb for IP+UDP headers valid for DHCP
  1490 + * @skb: the packet to check
  1491 + * @ip_src: a buffer to store the IPv4 source address in
  1492 + *
  1493 + * Checks whether the given skb has an IP and UDP header valid for a DHCP
  1494 + * message from a DHCP server. And if so, stores the IPv4 source address in
  1495 + * the provided buffer.
  1496 + *
  1497 + * Return: True if valid, false otherwise.
  1498 + */
  1499 +static bool
  1500 +batadv_dat_check_dhcp_ipudp(struct sk_buff *skb, __be32 *ip_src)
  1501 +{
  1502 + unsigned int offset = skb_network_offset(skb);
  1503 + struct udphdr *udphdr, _udphdr;
  1504 + struct iphdr *iphdr, _iphdr;
  1505 +
  1506 + iphdr = skb_header_pointer(skb, offset, sizeof(_iphdr), &_iphdr);
  1507 + if (!iphdr || iphdr->version != 4 || iphdr->ihl * 4 < sizeof(_iphdr))
  1508 + return false;
  1509 +
  1510 + if (iphdr->protocol != IPPROTO_UDP)
  1511 + return false;
  1512 +
  1513 + offset += iphdr->ihl * 4;
  1514 + skb_set_transport_header(skb, offset);
  1515 +
  1516 + udphdr = skb_header_pointer(skb, offset, sizeof(_udphdr), &_udphdr);
  1517 + if (!udphdr || udphdr->source != htons(67))
  1518 + return false;
  1519 +
  1520 + *ip_src = get_unaligned(&iphdr->saddr);
  1521 +
  1522 + return true;
  1523 +}
  1524 +
  1525 +/**
  1526 + * batadv_dat_check_dhcp() - examine packet for valid DHCP message
  1527 + * @skb: the packet to check
  1528 + * @proto: ethernet protocol hint (behind a potential vlan)
  1529 + * @ip_src: a buffer to store the IPv4 source address in
  1530 + *
  1531 + * Checks whether the given skb is a valid DHCP packet. And if so, stores the
  1532 + * IPv4 source address in the provided buffer.
  1533 + *
  1534 + * Caller needs to ensure that the skb network header is set correctly.
  1535 + *
  1536 + * Return: If skb is a valid DHCP packet, then returns its op code
  1537 + * (e.g. BOOTREPLY vs. BOOTREQUEST). Otherwise returns -EINVAL.
  1538 + */
  1539 +static int
  1540 +batadv_dat_check_dhcp(struct sk_buff *skb, __be16 proto, __be32 *ip_src)
  1541 +{
  1542 + __be32 *magic, _magic;
  1543 + unsigned int offset;
  1544 + struct {
  1545 + __u8 op;
  1546 + __u8 htype;
  1547 + __u8 hlen;
  1548 + __u8 hops;
  1549 + } *dhcp_h, _dhcp_h;
  1550 +
  1551 + if (proto != htons(ETH_P_IP))
  1552 + return -EINVAL;
  1553 +
  1554 + if (!batadv_dat_check_dhcp_ipudp(skb, ip_src))
  1555 + return -EINVAL;
  1556 +
  1557 + offset = skb_transport_offset(skb) + sizeof(struct udphdr);
  1558 + if (skb->len < offset + sizeof(struct batadv_dhcp_packet))
  1559 + return -EINVAL;
  1560 +
  1561 + dhcp_h = skb_header_pointer(skb, offset, sizeof(_dhcp_h), &_dhcp_h);
  1562 + if (!dhcp_h || dhcp_h->htype != BATADV_HTYPE_ETHERNET ||
  1563 + dhcp_h->hlen != ETH_ALEN)
  1564 + return -EINVAL;
  1565 +
  1566 + offset += offsetof(struct batadv_dhcp_packet, magic);
  1567 +
  1568 + magic = skb_header_pointer(skb, offset, sizeof(_magic), &_magic);
  1569 + if (!magic || get_unaligned(magic) != htonl(BATADV_DHCP_MAGIC))
  1570 + return -EINVAL;
  1571 +
  1572 + return dhcp_h->op;
  1573 +}
  1574 +
  1575 +/**
  1576 + * batadv_dat_get_dhcp_message_type() - get message type of a DHCP packet
  1577 + * @skb: the DHCP packet to parse
  1578 + *
  1579 + * Iterates over the DHCP options of the given DHCP packet to find a
  1580 + * DHCP Message Type option and parse it.
  1581 + *
  1582 + * Caller needs to ensure that the given skb is a valid DHCP packet and
  1583 + * that the skb transport header is set correctly.
  1584 + *
  1585 + * Return: The found DHCP message type value, if found. -EINVAL otherwise.
  1586 + */
  1587 +static int batadv_dat_get_dhcp_message_type(struct sk_buff *skb)
  1588 +{
  1589 + unsigned int offset = skb_transport_offset(skb) + sizeof(struct udphdr);
  1590 + u8 *type, _type;
  1591 + struct {
  1592 + u8 type;
  1593 + u8 len;
  1594 + } *tl, _tl;
  1595 +
  1596 + offset += sizeof(struct batadv_dhcp_packet);
  1597 +
  1598 + while ((tl = skb_header_pointer(skb, offset, sizeof(_tl), &_tl))) {
  1599 + if (tl->type == BATADV_DHCP_OPT_MSG_TYPE)
  1600 + break;
  1601 +
  1602 + if (tl->type == BATADV_DHCP_OPT_END)
  1603 + break;
  1604 +
  1605 + if (tl->type == BATADV_DHCP_OPT_PAD)
  1606 + offset++;
  1607 + else
  1608 + offset += tl->len + sizeof(_tl);
  1609 + }
  1610 +
  1611 + /* Option Overload Code not supported */
  1612 + if (!tl || tl->type != BATADV_DHCP_OPT_MSG_TYPE ||
  1613 + tl->len != sizeof(_type))
  1614 + return -EINVAL;
  1615 +
  1616 + offset += sizeof(_tl);
  1617 +
  1618 + type = skb_header_pointer(skb, offset, sizeof(_type), &_type);
  1619 + if (!type)
  1620 + return -EINVAL;
  1621 +
  1622 + return *type;
  1623 +}
  1624 +
  1625 +/**
  1626 + * batadv_dat_get_dhcp_yiaddr() - get yiaddr from a DHCP packet
  1627 + * @skb: the DHCP packet to parse
  1628 + * @buf: a buffer to store the yiaddr in
  1629 + *
  1630 + * Caller needs to ensure that the given skb is a valid DHCP packet and
  1631 + * that the skb transport header is set correctly.
  1632 + *
  1633 + * Return: True on success, false otherwise.
  1634 + */
  1635 +static bool batadv_dat_dhcp_get_yiaddr(struct sk_buff *skb, __be32 *buf)
  1636 +{
  1637 + unsigned int offset = skb_transport_offset(skb) + sizeof(struct udphdr);
  1638 + __be32 *yiaddr;
  1639 +
  1640 + offset += offsetof(struct batadv_dhcp_packet, yiaddr);
  1641 + yiaddr = skb_header_pointer(skb, offset, BATADV_DHCP_YIADDR_LEN, buf);
  1642 +
  1643 + if (!yiaddr)
  1644 + return false;
  1645 +
  1646 + if (yiaddr != buf)
  1647 + *buf = get_unaligned(yiaddr);
  1648 +
  1649 + return true;
  1650 +}
  1651 +
  1652 +/**
  1653 + * batadv_dat_get_dhcp_chaddr() - get chaddr from a DHCP packet
  1654 + * @skb: the DHCP packet to parse
  1655 + * @buf: a buffer to store the chaddr in
  1656 + *
  1657 + * Caller needs to ensure that the given skb is a valid DHCP packet and
  1658 + * that the skb transport header is set correctly.
  1659 + *
  1660 + * Return: True on success, false otherwise
  1661 + */
  1662 +static bool batadv_dat_get_dhcp_chaddr(struct sk_buff *skb, u8 *buf)
  1663 +{
  1664 + unsigned int offset = skb_transport_offset(skb) + sizeof(struct udphdr);
  1665 + u8 *chaddr;
  1666 +
  1667 + offset += offsetof(struct batadv_dhcp_packet, chaddr);
  1668 + chaddr = skb_header_pointer(skb, offset, BATADV_DHCP_CHADDR_LEN, buf);
  1669 +
  1670 + if (!chaddr)
  1671 + return false;
  1672 +
  1673 + if (chaddr != buf)
  1674 + memcpy(buf, chaddr, BATADV_DHCP_CHADDR_LEN);
  1675 +
  1676 + return true;
  1677 +}
  1678 +
  1679 +/**
  1680 + * batadv_dat_put_dhcp() - puts addresses from a DHCP packet into the DHT and
  1681 + * DAT cache
  1682 + * @bat_priv: the bat priv with all the soft interface information
  1683 + * @chaddr: the DHCP client MAC address
  1684 + * @yiaddr: the DHCP client IP address
  1685 + * @hw_dst: the DHCP server MAC address
  1686 + * @ip_dst: the DHCP server IP address
  1687 + * @vid: VLAN identifier
  1688 + *
  1689 + * Adds given MAC/IP pairs to the local DAT cache and propagates them further
  1690 + * into the DHT.
  1691 + *
  1692 + * For the DHT propagation, client MAC + IP will appear as the ARP Reply
  1693 + * transmitter (and hw_dst/ip_dst as the target).
  1694 + */
  1695 +static void batadv_dat_put_dhcp(struct batadv_priv *bat_priv, u8 *chaddr,
  1696 + __be32 yiaddr, u8 *hw_dst, __be32 ip_dst,
  1697 + unsigned short vid)
  1698 +{
  1699 + struct sk_buff *skb;
  1700 +
  1701 + skb = batadv_dat_arp_create_reply(bat_priv, yiaddr, ip_dst, chaddr,
  1702 + hw_dst, vid);
  1703 + if (!skb)
  1704 + return;
  1705 +
  1706 + skb_set_network_header(skb, ETH_HLEN);
  1707 +
  1708 + batadv_dat_entry_add(bat_priv, yiaddr, chaddr, vid);
  1709 + batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid);
  1710 +
  1711 + batadv_dat_send_data(bat_priv, skb, yiaddr, vid, BATADV_P_DAT_DHT_PUT);
  1712 + batadv_dat_send_data(bat_priv, skb, ip_dst, vid, BATADV_P_DAT_DHT_PUT);
  1713 +
  1714 + batadv_dbg(BATADV_DBG_DAT, bat_priv,
  1715 + "Snooped from outgoing DHCPACK (server address): %pI4, %pM (vid: %i)\n",
  1716 + &ip_dst, hw_dst, batadv_print_vid(vid));
  1717 + batadv_dbg(BATADV_DBG_DAT, bat_priv,
  1718 + "Snooped from outgoing DHCPACK (client address): %pI4, %pM (vid: %i)\n",
  1719 + &yiaddr, chaddr, batadv_print_vid(vid));
  1720 +}
  1721 +
  1722 +/**
  1723 + * batadv_dat_check_dhcp_ack() - examine packet for valid DHCP message
  1724 + * @skb: the packet to check
  1725 + * @proto: ethernet protocol hint (behind a potential vlan)
  1726 + * @ip_src: a buffer to store the IPv4 source address in
  1727 + * @chaddr: a buffer to store the DHCP Client Hardware Address in
  1728 + * @yiaddr: a buffer to store the DHCP Your IP Address in
  1729 + *
  1730 + * Checks whether the given skb is a valid DHCPACK. And if so, stores the
  1731 + * IPv4 server source address (ip_src), client MAC address (chaddr) and client
  1732 + * IPv4 address (yiaddr) in the provided buffers.
  1733 + *
  1734 + * Caller needs to ensure that the skb network header is set correctly.
  1735 + *
  1736 + * Return: True if the skb is a valid DHCPACK. False otherwise.
  1737 + */
  1738 +static bool
  1739 +batadv_dat_check_dhcp_ack(struct sk_buff *skb, __be16 proto, __be32 *ip_src,
  1740 + u8 *chaddr, __be32 *yiaddr)
  1741 +{
  1742 + int type;
  1743 +
  1744 + type = batadv_dat_check_dhcp(skb, proto, ip_src);
  1745 + if (type != BATADV_BOOTREPLY)
  1746 + return false;
  1747 +
  1748 + type = batadv_dat_get_dhcp_message_type(skb);
  1749 + if (type != BATADV_DHCPACK)
  1750 + return false;
  1751 +
  1752 + if (!batadv_dat_dhcp_get_yiaddr(skb, yiaddr))
  1753 + return false;
  1754 +
  1755 + if (!batadv_dat_get_dhcp_chaddr(skb, chaddr))
  1756 + return false;
  1757 +
  1758 + return true;
  1759 +}
  1760 +
  1761 +/**
  1762 + * batadv_dat_snoop_outgoing_dhcp_ack() - snoop DHCPACK and fill DAT with it
  1763 + * @bat_priv: the bat priv with all the soft interface information
  1764 + * @skb: the packet to snoop
  1765 + * @proto: ethernet protocol hint (behind a potential vlan)
  1766 + * @vid: VLAN identifier
  1767 + *
  1768 + * This function first checks whether the given skb is a valid DHCPACK. If
  1769 + * so then its source MAC and IP as well as its DHCP Client Hardware Address
  1770 + * field and DHCP Your IP Address field are added to the local DAT cache and
  1771 + * propagated into the DHT.
  1772 + *
  1773 + * Caller needs to ensure that the skb mac and network headers are set
  1774 + * correctly.
  1775 + */
  1776 +void batadv_dat_snoop_outgoing_dhcp_ack(struct batadv_priv *bat_priv,
  1777 + struct sk_buff *skb,
  1778 + __be16 proto,
  1779 + unsigned short vid)
  1780 +{
  1781 + u8 chaddr[BATADV_DHCP_CHADDR_LEN];
  1782 + __be32 ip_src, yiaddr;
  1783 +
  1784 + if (!atomic_read(&bat_priv->distributed_arp_table))
  1785 + return;
  1786 +
  1787 + if (!batadv_dat_check_dhcp_ack(skb, proto, &ip_src, chaddr, &yiaddr))
  1788 + return;
  1789 +
  1790 + batadv_dat_put_dhcp(bat_priv, chaddr, yiaddr, eth_hdr(skb)->h_source,
  1791 + ip_src, vid);
  1792 +}
  1793 +
  1794 +/**
  1795 + * batadv_dat_snoop_incoming_dhcp_ack() - snoop DHCPACK and fill DAT cache
  1796 + * @bat_priv: the bat priv with all the soft interface information
  1797 + * @skb: the packet to snoop
  1798 + * @hdr_size: header size, up to the tail of the batman-adv header
  1799 + *
  1800 + * This function first checks whether the given skb is a valid DHCPACK. If
  1801 + * so then its source MAC and IP as well as its DHCP Client Hardware Address
  1802 + * field and DHCP Your IP Address field are added to the local DAT cache.
  1803 + */
  1804 +void batadv_dat_snoop_incoming_dhcp_ack(struct batadv_priv *bat_priv,
  1805 + struct sk_buff *skb, int hdr_size)
  1806 +{
  1807 + u8 chaddr[BATADV_DHCP_CHADDR_LEN];
  1808 + struct ethhdr *ethhdr;
  1809 + __be32 ip_src, yiaddr;
  1810 + unsigned short vid;
  1811 + __be16 proto;
  1812 + u8 *hw_src;
  1813 +
  1814 + if (!atomic_read(&bat_priv->distributed_arp_table))
  1815 + return;
  1816 +
  1817 + if (unlikely(!pskb_may_pull(skb, hdr_size + ETH_HLEN)))
  1818 + return;
  1819 +
  1820 + ethhdr = (struct ethhdr *)(skb->data + hdr_size);
  1821 + skb_set_network_header(skb, hdr_size + ETH_HLEN);
  1822 + proto = ethhdr->h_proto;
  1823 +
  1824 + if (!batadv_dat_check_dhcp_ack(skb, proto, &ip_src, chaddr, &yiaddr))
  1825 + return;
  1826 +
  1827 + hw_src = ethhdr->h_source;
  1828 + vid = batadv_dat_get_vid(skb, &hdr_size);
  1829 +
  1830 + batadv_dat_entry_add(bat_priv, yiaddr, chaddr, vid);
  1831 + batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
  1832 +
  1833 + batadv_dbg(BATADV_DBG_DAT, bat_priv,
  1834 + "Snooped from incoming DHCPACK (server address): %pI4, %pM (vid: %i)\n",
  1835 + &ip_src, hw_src, batadv_print_vid(vid));
  1836 + batadv_dbg(BATADV_DBG_DAT, bat_priv,
  1837 + "Snooped from incoming DHCPACK (client address): %pI4, %pM (vid: %i)\n",
  1838 + &yiaddr, chaddr, batadv_print_vid(vid));
1440 1839 }
1441 1840  
1442 1841 /**
net/batman-adv/distributed-arp-table.h
... ... @@ -46,6 +46,12 @@
46 46 struct sk_buff *skb);
47 47 bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
48 48 struct sk_buff *skb, int hdr_size);
  49 +void batadv_dat_snoop_outgoing_dhcp_ack(struct batadv_priv *bat_priv,
  50 + struct sk_buff *skb,
  51 + __be16 proto,
  52 + unsigned short vid);
  53 +void batadv_dat_snoop_incoming_dhcp_ack(struct batadv_priv *bat_priv,
  54 + struct sk_buff *skb, int hdr_size);
49 55 bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
50 56 struct batadv_forw_packet *forw_packet);
51 57  
... ... @@ -138,6 +144,19 @@
138 144 struct sk_buff *skb, int hdr_size)
139 145 {
140 146 return false;
  147 +}
  148 +
  149 +static inline void
  150 +batadv_dat_snoop_outgoing_dhcp_ack(struct batadv_priv *bat_priv,
  151 + struct sk_buff *skb, __be16 proto,
  152 + unsigned short vid)
  153 +{
  154 +}
  155 +
  156 +static inline void
  157 +batadv_dat_snoop_incoming_dhcp_ack(struct batadv_priv *bat_priv,
  158 + struct sk_buff *skb, int hdr_size)
  159 +{
141 160 }
142 161  
143 162 static inline bool
net/batman-adv/routing.c
... ... @@ -1043,6 +1043,8 @@
1043 1043 hdr_size))
1044 1044 goto rx_success;
1045 1045  
  1046 + batadv_dat_snoop_incoming_dhcp_ack(bat_priv, skb, hdr_size);
  1047 +
1046 1048 batadv_interface_rx(recv_if->soft_iface, skb, hdr_size,
1047 1049 orig_node);
1048 1050  
... ... @@ -1277,6 +1279,8 @@
1277 1279 goto rx_success;
1278 1280 if (batadv_dat_snoop_incoming_arp_reply(bat_priv, skb, hdr_size))
1279 1281 goto rx_success;
  1282 +
  1283 + batadv_dat_snoop_incoming_dhcp_ack(bat_priv, skb, hdr_size);
1280 1284  
1281 1285 /* broadcast for me */
1282 1286 batadv_interface_rx(recv_if->soft_iface, skb, hdr_size, orig_node);
net/batman-adv/soft-interface.c
... ... @@ -212,6 +212,7 @@
212 212 enum batadv_forw_mode forw_mode;
213 213 struct batadv_orig_node *mcast_single_orig = NULL;
214 214 int network_offset = ETH_HLEN;
  215 + __be16 proto;
215 216  
216 217 if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
217 218 goto dropped;
218 219  
219 220  
... ... @@ -223,12 +224,15 @@
223 224 vid = batadv_get_vid(skb, 0);
224 225 ethhdr = eth_hdr(skb);
225 226  
226   - switch (ntohs(ethhdr->h_proto)) {
  227 + proto = ethhdr->h_proto;
  228 +
  229 + switch (ntohs(proto)) {
227 230 case ETH_P_8021Q:
228 231 vhdr = vlan_eth_hdr(skb);
  232 + proto = vhdr->h_vlan_encapsulated_proto;
229 233  
230 234 /* drop batman-in-batman packets to prevent loops */
231   - if (vhdr->h_vlan_encapsulated_proto != htons(ETH_P_BATMAN)) {
  235 + if (proto != htons(ETH_P_BATMAN)) {
232 236 network_offset += VLAN_HLEN;
233 237 break;
234 238 }
... ... @@ -255,6 +259,9 @@
255 259 if (!client_added)
256 260 goto dropped;
257 261 }
  262 +
  263 + /* Snoop address candidates from DHCPACKs for early DAT filling */
  264 + batadv_dat_snoop_outgoing_dhcp_ack(bat_priv, skb, proto, vid);
258 265  
259 266 /* don't accept stp packets. STP does not help in meshes.
260 267 * better use the bridge loop avoidance ...