From patchwork Mon May 7 01:38:22 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Linus_L=C3=BCssing?= X-Patchwork-Id: 17339 X-Patchwork-Delegate: sw@simonwunderlich.de Return-Path: X-Original-To: patchwork@open-mesh.org Delivered-To: patchwork@open-mesh.org Received: from open-mesh.org (localhost [IPv6:::1]) by open-mesh.org (Postfix) with ESMTP id 05A8B82346; Mon, 7 May 2018 03:38:40 +0200 (CEST) Received-SPF: None (mailfrom) identity=mailfrom; client-ip=2a01:4f8:171:314c::100:a1; helo=mail.aperture-lab.de; envelope-from=linus.luessing@c0d3.blue; receiver= X-Greylist: delayed 122871 seconds by postgrey-1.36 at open-mesh.org; Mon, 07 May 2018 03:38:38 CEST Received: from mail.aperture-lab.de (mail.aperture-lab.de [IPv6:2a01:4f8:171:314c::100:a1]) by open-mesh.org (Postfix) with ESMTPS id 1A9118231E for ; Mon, 7 May 2018 03:38:37 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by mail.aperture-lab.de (Postfix) with ESMTP id C7D2EE04F1; Mon, 7 May 2018 03:38:37 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at aperture-lab.de Received: from mail.aperture-lab.de ([127.0.0.1]) by localhost (mail.aperture-lab.de [127.0.0.1]) (amavisd-new, port 10025) with ESMTP id CtMOhp76PhnI; Mon, 7 May 2018 03:38:37 +0200 (CEST) Received: from localhost (unknown [IPv6:2a01:170:1112:0:bcd7:94ff:fefd:2ddd]) (Authenticated sender: linus.luessing@c0d3.blue) by mail.aperture-lab.de (Postfix) with ESMTPSA; Mon, 7 May 2018 03:38:36 +0200 (CEST) From: =?utf-8?q?Linus_L=C3=BCssing?= To: b.a.t.m.a.n@lists.open-mesh.org Date: Mon, 7 May 2018 03:38:22 +0200 Message-Id: <20180507013823.739-2-linus.luessing@c0d3.blue> X-Mailer: git-send-email 2.17.0 In-Reply-To: <20180507013823.739-1-linus.luessing@c0d3.blue> References: <20180507013823.739-1-linus.luessing@c0d3.blue> MIME-Version: 1.0 Subject: [B.A.T.M.A.N.] [PATCH v3 1/2] batman-adv: Snoop DHCPACKs for DAT X-BeenThere: b.a.t.m.a.n@lists.open-mesh.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: The list for a Better Approach To Mobile Ad-hoc Networking List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: The list for a Better Approach To Mobile Ad-hoc Networking Errors-To: b.a.t.m.a.n-bounces@lists.open-mesh.org Sender: "B.A.T.M.A.N" 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 eliminates the need for mesh wide message flooding for IPv4 address resolution. Signed-off-by: Linus Lüssing --- Changes in v3: * Rebase to master * Shortened commit message Changes in v2: * Rebase to master * Fix compilation with CONFIG_BATMAN_ADV=n (added stubs) Changes in RFC -> non-RFC * Added kerneldoc * Added Signed-off-by * More IP Header checks (iph->hlen considered, ip version checked, ...) * Parsing & checking DHCP Message Type Option, only snooping DHCPACKs now * Moved ethernet protocol check from batadv_dat_check_dhcp to batadv_dat_check_dhcp_ipudp * Removed buffer-length parameter from batadv_dat_dhcp_get_{yiaddr,chaddr}() * Renamed batadv_dat_put() to batadv_dat_put_pairs() --- include/uapi/linux/batadv_packet.h | 48 ++++++ net/batman-adv/distributed-arp-table.c | 296 +++++++++++++++++++++++++++++++++ net/batman-adv/distributed-arp-table.h | 11 ++ net/batman-adv/soft-interface.c | 11 +- 4 files changed, 364 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/batadv_packet.h b/include/uapi/linux/batadv_packet.h index 894d8d2f..7ef7b519 100644 --- a/include/uapi/linux/batadv_packet.h +++ b/include/uapi/linux/batadv_packet.h @@ -630,6 +630,54 @@ struct batadv_tvlv_mcast_data { __u8 reserved[3]; }; +enum batadv_bootpop { + BATADV_BOOTREQUEST = 1, + BATADV_BOOTREPLY = 2, +}; + +enum batadv_boothtype { + BATADV_HTYPE_ETHERNET = 1, +}; + +enum batadv_dhcpoptioncode { + BATADV_DHCP_OPT_PAD = 0, + BATADV_DHCP_OPT_MSG_TYPE = 53, + BATADV_DHCP_OPT_END = 255, +}; + +enum batadv_dhcptype { + BATADV_DHCPDISCOVER = 1, + BATADV_DHCPOFFER = 2, + BATADV_DHCPREQUEST = 3, + BATADV_DHCPDECLINE = 4, + BATADV_DHCPACK = 5, + BATADV_DHCPNAK = 6, + BATADV_DHCPRELEASE = 7, + BATADV_DHCPINFORM = 8, +}; + +/* { 99, 130, 83, 99 } */ +#define BATADV_DHCP_MAGIC 1669485411 + +struct batadv_dhcp_packet { + __u8 op; + __u8 htype; + __u8 hlen; + __u8 hops; + __be32 xid; + __be16 secs; + __be16 flags; + __be32 ciaddr; + __be32 yiaddr; + __be32 siaddr; + __be32 giaddr; + __u8 chaddr[16]; + __u8 sname[64]; + __u8 file[128]; + __be32 magic; + __u8 options[0]; +}; + #pragma pack() #endif /* _UAPI_LINUX_BATADV_PACKET_H_ */ diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index a60bacf7..00b96379 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -42,9 +43,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -1130,6 +1133,7 @@ batadv_dat_arp_create_reply(struct batadv_priv *bat_priv, __be32 ip_src, return NULL; skb_reset_mac_header(skb); + skb_set_network_header(skb, ETH_HLEN); if (vid & BATADV_VLAN_HAS_TAG) skb = vlan_insert_tag(skb, htons(ETH_P_8021Q), @@ -1436,6 +1440,298 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, } /** + * batadv_dat_check_dhcp_ipudp() - check skb for IP+UDP headers valid for DHCP + * @skb: the packet to check + * @proto: ethernet protocol hint (behind a potential vlan) + * + * Checks whether the given skb has an IP and UDP header valid for a DHCP + * message from a DHCP server. + * + * Return: True if valid, false otherwise. + */ +static bool batadv_dat_check_dhcp_ipudp(struct sk_buff *skb) +{ + struct iphdr *iphdr, _iphdr; + struct udphdr *udphdr, _udphdr; + unsigned int offset = skb_network_offset(skb); + + iphdr = skb_header_pointer(skb, offset, sizeof(_iphdr), &_iphdr); + if (!iphdr || iphdr->version != 4 || ip_hdrlen(skb) < sizeof(_iphdr)) + return false; + + if (iphdr->protocol != IPPROTO_UDP) + return false; + + offset += ip_hdrlen(skb); + skb_set_transport_header(skb, offset); + + udphdr = skb_header_pointer(skb, offset, sizeof(_udphdr), &_udphdr); + if (!udphdr || udphdr->source != htons(67)) + return false; + + return true; +} + +/** + * batadv_dat_check_dhcp() - examine packet for valid DHCP message + * @skb: the packet to check + * @proto: ethernet protocol hint (behind a potential vlan) + * + * Checks whether the given skb is a valid DHCP packet. + * + * Caller needs to ensure that the skb network header is set correctly. + * + * Return: If skb is a valid DHCP packet, then returns its op code + * (e.g. BOOTREPLY vs. BOOTREQUEST). Otherwise returns -EINVAL. + */ +static int batadv_dat_check_dhcp(struct sk_buff *skb, __be16 proto) +{ + u8 *op, _op; + u8 *htype, _htype; + u8 *hlen, _hlen; + __be32 *magic, _magic; + unsigned int dhcp_offset; + unsigned int offset; + + if (proto != htons(ETH_P_IP)) + return -EINVAL; + + if (!batadv_dat_check_dhcp_ipudp(skb)) + return -EINVAL; + + dhcp_offset = skb_transport_offset(skb) + sizeof(struct udphdr); + if (skb->len < dhcp_offset + sizeof(struct batadv_dhcp_packet)) + return -EINVAL; + + offset = dhcp_offset + offsetof(struct batadv_dhcp_packet, op); + + op = skb_header_pointer(skb, offset, sizeof(_op), &_op); + if (!op) + return -EINVAL; + + offset = dhcp_offset + offsetof(struct batadv_dhcp_packet, htype); + + htype = skb_header_pointer(skb, offset, sizeof(_htype), &_htype); + if (!htype || *htype != BATADV_HTYPE_ETHERNET) + return -EINVAL; + + offset = dhcp_offset + offsetof(struct batadv_dhcp_packet, hlen); + + hlen = skb_header_pointer(skb, offset, sizeof(_hlen), &_hlen); + if (!hlen || *hlen != ETH_ALEN) + return -EINVAL; + + offset = dhcp_offset + offsetof(struct batadv_dhcp_packet, magic); + + magic = skb_header_pointer(skb, offset, sizeof(_magic), &_magic); + if (!magic || *magic != htonl(BATADV_DHCP_MAGIC)) + return -EINVAL; + + return *op; +} + +/** + * batadv_dat_get_dhcp_message_type() - get message type of a DHCP packet + * @skb: the DHCP packet to parse + * + * Iterates over the DHCP options of the given DHCP packet to find a + * DHCP Message Type option and parse it. + * + * Caller needs to ensure that the given skb is a valid DHCP packet and + * that the skb transport header is set correctly. + * + * Return: The found DHCP message type value, if found. -EINVAL otherwise. + */ +static int batadv_dat_get_dhcp_message_type(struct sk_buff *skb) +{ + unsigned int offset = skb_transport_offset(skb) + sizeof(struct udphdr); + u8 *type, _type; + struct { + u8 type; + u8 len; + } *tl, _tl; + + offset += sizeof(struct batadv_dhcp_packet); + + while ((tl = skb_header_pointer(skb, offset, sizeof(_tl), &_tl))) { + if (tl->type == BATADV_DHCP_OPT_MSG_TYPE) + break; + + if (tl->type == BATADV_DHCP_OPT_END) + break; + + if (tl->type == BATADV_DHCP_OPT_PAD) + offset++; + else + offset += tl->len + sizeof(_tl); + } + + /* Option Overload Code not supported */ + if (!tl || tl->type != BATADV_DHCP_OPT_MSG_TYPE || + tl->len != sizeof(_type)) + return -EINVAL; + + offset += sizeof(_tl); + + type = skb_header_pointer(skb, offset, sizeof(_type), &_type); + if (!type) + return -EINVAL; + + return *type; +} + +/** + * batadv_dat_get_dhcp_yiaddr() - get yiaddr from a DHCP packet + * @skb: the DHCP packet to parse + * @buffer: a buffer to store the yiaddr in (if necessary / skb is non-linear) + * + * Caller needs to ensure that the given skb is a valid DHCP packet and + * that the skb transport header is set correctly. + * + * Return: A safely accessible "Your IP Address" field from the provided DHCP + * packet. + */ +static __be32 *batadv_dat_dhcp_get_yiaddr(struct sk_buff *skb, __be32 *buffer) +{ + unsigned int offset = skb_transport_offset(skb) + sizeof(struct udphdr); + unsigned int len = sizeof(((struct batadv_dhcp_packet *)0)->yiaddr); + + offset += offsetof(struct batadv_dhcp_packet, yiaddr); + + return skb_header_pointer(skb, offset, len, buffer); +} + +/** + * batadv_dat_get_dhcp_chaddr() - get chaddr from a DHCP packet + * @skb: the DHCP packet to parse + * @buffer: a buffer to store the chaddr in (if necessary / skb is non-linear) + * + * Caller needs to ensure that the given skb is a valid DHCP packet and + * that the skb transport header is set correctly. + * + * Return: A safely accessible "Client Hardware Address" field from the provided + * DHCP packet. + */ +static u8 *batadv_dat_get_dhcp_chaddr(struct sk_buff *skb, u8 *buffer) +{ + unsigned int offset = skb_transport_offset(skb) + sizeof(struct udphdr); + unsigned int len = sizeof(((struct batadv_dhcp_packet *)0)->chaddr); + + offset += offsetof(struct batadv_dhcp_packet, chaddr); + + return skb_header_pointer(skb, offset, len, buffer); +} + +/** + * batadv_dat_put_pairs() - puts two MAC/IP pairs into the DHT and DAT cache + * @bat_priv: the bat priv with all the soft interface information + * @hw_src: first value of DHT and ARP sender MAC + * @ip_src: first key of DHT and ARP sender IP + * @hw_dst: second value of DHT and ARP target MAC + * @ip_dst: second key of DHT and ARP target IP + * @vid: VLAN identifier + * + * First checks whether the given MAC/IP pairs are suitable for DAT. If so, adds + * them to the local DAT cache and propagates them further into the DHT. + * + * For the DHT propagation, hw_src/ip_src will appear as the ARP Reply + * transmitter (and hw_dst/ip_dst as the target). + * + * Return: True on success, false otherwise. + */ +static bool batadv_dat_put_pairs(struct batadv_priv *bat_priv, u8 *hw_src, + __be32 ip_src, u8 *hw_dst, __be32 ip_dst, + unsigned short vid) +{ + struct sk_buff *skb; + int hdr_size; + u16 type; + int ret = false; + + skb = batadv_dat_arp_create_reply(bat_priv, ip_src, ip_dst, hw_src, + hw_dst, vid); + if (!skb) + return false; + + /* Check for validity of provided addresses */ + hdr_size = skb_network_offset(skb) - ETH_HLEN; + type = batadv_arp_get_type(bat_priv, skb, hdr_size); + if (type != ARPOP_REPLY) + goto err_skip_commit; + + batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid); + batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid); + + batadv_dat_send_data(bat_priv, skb, ip_src, vid, BATADV_P_DAT_DHT_PUT); + batadv_dat_send_data(bat_priv, skb, ip_dst, vid, BATADV_P_DAT_DHT_PUT); + + ret = true; + +err_skip_commit: + dev_kfree_skb(skb); + return ret; +} + +/** + * batadv_dat_snoop_outgoing_dhcp_ack() - snoop DHCPACK and fill DAT with it + * @bat_priv: the bat priv with all the soft interface information + * @skb: the packet to snoop + * @proto: ethernet protocol hint (behind a potential vlan) + * @vid: VLAN identifier + * + * This function first checks whether the given skb is a valid DHCPACK. If + * so then its source MAC and IP as well as its DHCP Client Hardware Address + * field and DHCP Your IP Address field are added to the local DAT cache and + * propagated into the DHT. + * + * Caller needs to ensure that the skb mac and network headers are set + * correctly. + */ +void batadv_dat_snoop_outgoing_dhcp_ack(struct batadv_priv *bat_priv, + struct sk_buff *skb, + __be16 proto, + unsigned short vid) +{ + int type; + u8 *chaddr, _chaddr[ETH_ALEN]; + __be32 *yiaddr, _yiaddr; + + if (!atomic_read(&bat_priv->distributed_arp_table)) + return; + + if (batadv_dat_check_dhcp(skb, proto) != BATADV_BOOTREPLY) + return; + + type = batadv_dat_get_dhcp_message_type(skb); + if (type != BATADV_DHCPACK) + return; + + yiaddr = batadv_dat_dhcp_get_yiaddr(skb, &_yiaddr); + if (!yiaddr) + return; + + chaddr = batadv_dat_get_dhcp_chaddr(skb, _chaddr); + if (!chaddr) + return; + + /* ARP sender MAC + IP -> DHCP Client (chaddr+yiaddr), + * ARP target MAC + IP -> DHCP Server (ethhdr/iphdr sources) + */ + if (!batadv_dat_put_pairs(bat_priv, chaddr, *yiaddr, + eth_hdr(skb)->h_source, ip_hdr(skb)->saddr, + vid)) + return; + + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "Snooped from DHCPACK (server-side): %pI4, %pM (vid: %i)\n", + &ip_hdr(skb)->saddr, eth_hdr(skb)->h_source, + batadv_print_vid(vid)); + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "Snooped from DHCPACK (client-side): %pI4, %pM (vid: %i)\n", + yiaddr, chaddr, batadv_print_vid(vid)); +} + +/** * batadv_dat_drop_broadcast_packet() - check if an ARP request has to be * dropped (because the node has already obtained the reply via DAT) or not * @bat_priv: the bat priv with all the soft interface information diff --git a/net/batman-adv/distributed-arp-table.h b/net/batman-adv/distributed-arp-table.h index a0459602..cf58d230 100644 --- a/net/batman-adv/distributed-arp-table.h +++ b/net/batman-adv/distributed-arp-table.h @@ -46,6 +46,10 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv, struct sk_buff *skb); bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, struct sk_buff *skb, int hdr_size); +void batadv_dat_snoop_outgoing_dhcp_ack(struct batadv_priv *bat_priv, + struct sk_buff *skb, + __be16 proto, + unsigned short vid); bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv, struct batadv_forw_packet *forw_packet); @@ -140,6 +144,13 @@ batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, return false; } +static inline void +batadv_dat_snoop_outgoing_dhcp_ack(struct batadv_priv *bat_priv, + struct sk_buff *skb, __be16 proto, + unsigned short vid) +{ +} + static inline bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv, struct batadv_forw_packet *forw_packet) diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 1485263a..a8559629 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -212,6 +212,7 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb, enum batadv_forw_mode forw_mode; struct batadv_orig_node *mcast_single_orig = NULL; int network_offset = ETH_HLEN; + __be16 proto; if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) goto dropped; @@ -223,12 +224,15 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb, vid = batadv_get_vid(skb, 0); ethhdr = eth_hdr(skb); - switch (ntohs(ethhdr->h_proto)) { + proto = ethhdr->h_proto; + + switch (ntohs(proto)) { case ETH_P_8021Q: vhdr = vlan_eth_hdr(skb); + proto = vhdr->h_vlan_encapsulated_proto; /* drop batman-in-batman packets to prevent loops */ - if (vhdr->h_vlan_encapsulated_proto != htons(ETH_P_BATMAN)) { + if (proto != htons(ETH_P_BATMAN)) { network_offset += VLAN_HLEN; break; } @@ -256,6 +260,9 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb, goto dropped; } + /* Snoop address candidates from DHCPACKs for early DAT filling */ + batadv_dat_snoop_outgoing_dhcp_ack(bat_priv, skb, proto, vid); + /* don't accept stp packets. STP does not help in meshes. * better use the bridge loop avoidance ... * From patchwork Mon May 7 01:38:23 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Linus_L=C3=BCssing?= X-Patchwork-Id: 17340 X-Patchwork-Delegate: sw@simonwunderlich.de Return-Path: X-Original-To: patchwork@open-mesh.org Delivered-To: patchwork@open-mesh.org Received: from open-mesh.org (localhost [IPv6:::1]) by open-mesh.org (Postfix) with ESMTP id 5009F823A2; Mon, 7 May 2018 03:38:43 +0200 (CEST) Received-SPF: None (mailfrom) identity=mailfrom; client-ip=2a01:4f8:171:314c::100:a1; helo=mail.aperture-lab.de; envelope-from=linus.luessing@c0d3.blue; receiver= Received: from mail.aperture-lab.de (mail.aperture-lab.de [IPv6:2a01:4f8:171:314c::100:a1]) by open-mesh.org (Postfix) with ESMTPS id 9F45E8234C for ; Mon, 7 May 2018 03:38:40 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by mail.aperture-lab.de (Postfix) with ESMTP id 1CB5AE04F1; Mon, 7 May 2018 03:38:40 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at aperture-lab.de Received: from mail.aperture-lab.de ([127.0.0.1]) by localhost (mail.aperture-lab.de [127.0.0.1]) (amavisd-new, port 10025) with ESMTP id KZz7-ZI6FV2P; Mon, 7 May 2018 03:38:39 +0200 (CEST) Received: from localhost (unknown [IPv6:2a01:170:1112:0:bcd7:94ff:fefd:2ddd]) (Authenticated sender: linus.luessing@c0d3.blue) by mail.aperture-lab.de (Postfix) with ESMTPSA; Mon, 7 May 2018 03:38:39 +0200 (CEST) From: =?utf-8?q?Linus_L=C3=BCssing?= To: b.a.t.m.a.n@lists.open-mesh.org Date: Mon, 7 May 2018 03:38:23 +0200 Message-Id: <20180507013823.739-3-linus.luessing@c0d3.blue> X-Mailer: git-send-email 2.17.0 In-Reply-To: <20180507013823.739-1-linus.luessing@c0d3.blue> References: <20180507013823.739-1-linus.luessing@c0d3.blue> MIME-Version: 1.0 Subject: [B.A.T.M.A.N.] [PATCH v3 2/2] batman-adv: Introduce no noflood mark X-BeenThere: b.a.t.m.a.n@lists.open-mesh.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: The list for a Better Approach To Mobile Ad-hoc Networking List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: The list for a Better Approach To Mobile Ad-hoc Networking Errors-To: b.a.t.m.a.n-bounces@lists.open-mesh.org Sender: "B.A.T.M.A.N" This mark prevents a multicast packet being flooded through the whole mesh. The advantage of marking certain multicast packets via e.g. ebtables instead of dropping is then the following: This allows an administrator to let specific multicast packets pass as long as they are forwarded to a limited number of nodes only and are therefore creating no burdon to unrelated nodes. For instance when applied to ARP Requests then this allows ARP Requests to be forwarded to the three DHT candidates via unicast. But it disables the broadcast fallback if the unicast attempts were left unanswered. Signed-off-by: Linus Lüssing --- Changes in v3: * Newly added --- Documentation/ABI/testing/sysfs-class-net-mesh | 8 ++ net/batman-adv/soft-interface.c | 20 +++++ net/batman-adv/sysfs.c | 103 +++++++++++++++++++++---- net/batman-adv/types.h | 12 +++ 4 files changed, 128 insertions(+), 15 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-net-mesh b/Documentation/ABI/testing/sysfs-class-net-mesh index c2b956d4..93fc8d03 100644 --- a/Documentation/ABI/testing/sysfs-class-net-mesh +++ b/Documentation/ABI/testing/sysfs-class-net-mesh @@ -76,6 +76,14 @@ Description: is used to classify clients as "isolated" by the Extended Isolation feature. +What: /sys/class/net//mesh/noflood_mark +Date: Mar 2018 +Contact: Linus Lüssing +Description: + Defines the noflood mark (and its bitmask) which + will drop frames with a matching mark if they were + to be flooded. + What: /sys/class/net//mesh/multicast_mode Date: Feb 2014 Contact: Linus Lüssing diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index a8559629..e9fe6388 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -188,6 +188,23 @@ static void batadv_interface_set_rx_mode(struct net_device *dev) { } +/** + * batadv_send_skb_has_noflood_mark() - check if packet has a noflood mark + * @bat_priv: the bat priv with all the soft interface information + * @skb: the packet to check + * + * Return: True if the skb's mark matches a configured noflood mark and + * noflood mark mask. False otherwise. + */ +static bool +batadv_skb_has_noflood_mark(struct batadv_priv *bat_priv, struct sk_buff *skb) +{ + u32 match_mark = skb->mark & bat_priv->noflood_mark_mask; + + return bat_priv->noflood_mark_mask && + match_mark == bat_priv->noflood_mark; +} + static netdev_tx_t batadv_interface_tx(struct sk_buff *skb, struct net_device *soft_iface) { @@ -333,6 +350,9 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb, if (batadv_dat_snoop_outgoing_arp_request(bat_priv, skb)) brd_delay = msecs_to_jiffies(ARP_REQ_DELAY); + if (batadv_skb_has_noflood_mark(bat_priv, skb)) + goto dropped; + if (batadv_skb_head_push(skb, sizeof(*bcast_packet)) < 0) goto dropped; diff --git a/net/batman-adv/sysfs.c b/net/batman-adv/sysfs.c index f2eef43b..7b57e4b4 100644 --- a/net/batman-adv/sysfs.c +++ b/net/batman-adv/sysfs.c @@ -618,28 +618,24 @@ static ssize_t batadv_show_isolation_mark(struct kobject *kobj, } /** - * batadv_store_isolation_mark() - parse and store the isolation mark/mask - * entered by the user - * @kobj: kobject representing the private mesh sysfs directory - * @attr: the batman-adv attribute the user is interacting with + * batadv_store_parse_mark() - parse a mark and mask * @buff: the buffer containing the user data - * @count: number of bytes in the buffer + * @mark: the variable to store the mark in + * @mask: the variable to store the mask in * - * Return: 'count' on success or a negative error code in case of failure + * Parses a string for a mark and mask. The format is expected to consist of + * two 32 bit hexadecimal numbers delimited by a '/'. + * + * Return: 0 on success, -EINVAL on error. */ -static ssize_t batadv_store_isolation_mark(struct kobject *kobj, - struct attribute *attr, char *buff, - size_t count) +static int batadv_store_parse_mark(char *buff, u32 *mark, u32 *mask) { - struct net_device *net_dev = batadv_kobj_to_netdev(kobj); - struct batadv_priv *bat_priv = netdev_priv(net_dev); - u32 mark, mask; char *mask_ptr; /* parse the mask if it has been specified, otherwise assume the mask is * the biggest possible */ - mask = 0xFFFFFFFF; + *mask = 0xFFFFFFFF; mask_ptr = strchr(buff, '/'); if (mask_ptr) { *mask_ptr = '\0'; @@ -648,12 +644,36 @@ static ssize_t batadv_store_isolation_mark(struct kobject *kobj, /* the mask must be entered in hex base as it is going to be a * bitmask and not a prefix length */ - if (kstrtou32(mask_ptr, 16, &mask) < 0) + if (kstrtou32(mask_ptr, 16, mask) < 0) return -EINVAL; } /* the mark can be entered in any base */ - if (kstrtou32(buff, 0, &mark) < 0) + if (kstrtou32(buff, 0, mark) < 0) + return -EINVAL; + + return 0; +} + +/** + * batadv_store_isolation_mark() - parse and store the isolation mark/mask + * entered by the user + * @kobj: kobject representing the private mesh sysfs directory + * @attr: the batman-adv attribute the user is interacting with + * @buff: the buffer containing the user data + * @count: number of bytes in the buffer + * + * Return: 'count' on success or a negative error code in case of failure + */ +static ssize_t batadv_store_isolation_mark(struct kobject *kobj, + struct attribute *attr, char *buff, + size_t count) +{ + struct net_device *net_dev = batadv_kobj_to_netdev(kobj); + struct batadv_priv *bat_priv = netdev_priv(net_dev); + u32 mark, mask; + + if (batadv_store_parse_mark(buff, &mark, &mask) < 0) return -EINVAL; bat_priv->isolation_mark_mask = mask; @@ -667,6 +687,56 @@ static ssize_t batadv_store_isolation_mark(struct kobject *kobj, return count; } +/** + * batadv_show_noflood_mark() - print the current noflood mark/mask + * @kobj: kobject representing the private mesh sysfs directory + * @attr: the batman-adv attribute the user is interacting with + * @buff: the buffer that will contain the data to send back to the user + * + * Return: the number of bytes written into 'buff' on success or a negative + * error code in case of failure + */ +static ssize_t batadv_show_noflood_mark(struct kobject *kobj, + struct attribute *attr, char *buff) +{ + struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj); + + return sprintf(buff, "%#.8x/%#.8x\n", bat_priv->noflood_mark, + bat_priv->noflood_mark_mask); +} + +/** + * batadv_store_noflood_mark() - parse and store the noflood mark/mask + * entered by the user + * @kobj: kobject representing the private mesh sysfs directory + * @attr: the batman-adv attribute the user is interacting with + * @buff: the buffer containing the user data + * @count: number of bytes in the buffer + * + * Return: 'count' on success or a negative error code in case of failure + */ +static ssize_t batadv_store_noflood_mark(struct kobject *kobj, + struct attribute *attr, char *buff, + size_t count) +{ + struct net_device *net_dev = batadv_kobj_to_netdev(kobj); + struct batadv_priv *bat_priv = netdev_priv(net_dev); + u32 mark, mask; + + if (batadv_store_parse_mark(buff, &mark, &mask) < 0) + return -EINVAL; + + bat_priv->noflood_mark_mask = mask; + /* erase bits not covered by the mask */ + bat_priv->noflood_mark = mark & bat_priv->noflood_mark_mask; + + batadv_info(net_dev, + "New skb noflood mark: %#.8x/%#.8x\n", + bat_priv->noflood_mark, bat_priv->noflood_mark_mask); + + return count; +} + BATADV_ATTR_SIF_BOOL(aggregated_ogms, 0644, NULL); BATADV_ATTR_SIF_BOOL(bonding, 0644, NULL); #ifdef CONFIG_BATMAN_ADV_BLA @@ -697,6 +767,8 @@ BATADV_ATTR_SIF_BOOL(network_coding, 0644, batadv_nc_status_update); #endif static BATADV_ATTR(isolation_mark, 0644, batadv_show_isolation_mark, batadv_store_isolation_mark); +static BATADV_ATTR(noflood_mark, 0644, batadv_show_noflood_mark, + batadv_store_noflood_mark); static struct batadv_attribute *batadv_mesh_attrs[] = { &batadv_attr_aggregated_ogms, @@ -724,6 +796,7 @@ static struct batadv_attribute *batadv_mesh_attrs[] = { &batadv_attr_network_coding, #endif &batadv_attr_isolation_mark, + &batadv_attr_noflood_mark, NULL, }; diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 360357f8..7d5d9987 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1584,6 +1584,18 @@ struct batadv_priv { */ u32 isolation_mark_mask; + /** + * @noflood_mark: the skb->mark value used to allow directed targeting + * only + */ + u32 noflood_mark; + + /** + * @noflood_mark_mask: bitmask identifying the bits in skb->mark to be + * used for the noflood mark + */ + u32 noflood_mark_mask; + /** @bcast_seqno: last sent broadcast packet sequence number */ atomic_t bcast_seqno;