View Issue Details

IDProjectCategoryView StatusLast Update
0000039Freifunk Franken FirmwareGeneralpublic2019-10-02 12:49
ReporterChristianD Assigned To 
PrioritynormalSeverityminorReproducibilityhave not tried
Status closedResolutionfixed 
Product Version20170218-alpha 
Summary0000039: l2tp Traffic overflow
Descriptionhttps://github.com/FreifunkFranken/firmware/commit/fce5b0ceefa9fdddd3f1b690416659785df4e2fc#diff-055d355e728cddb080887618826715a5

Der Bug scheint im LEDE wieder aufzutreten. Vermutlich brauchen wir das Patch auch im LEDE
TagsNo tags attached.
Attached Files

Activities

ChristianD

2017-05-26 14:15

manager   ~0000083

Ich habe versucht den alten Patch

https://github.com/FreifunkFranken/firmware/commit/fce5b0ceefa9fdddd3f1b690416659785df4e2fc#diff-055d355e728cddb080887618826715a5

auf das Verzeichnis

target/linux/ar71xx/patches-4.4/fix-l2tp-stats-couter-on-32-Bit-Systems.patch

umzubiegen damit es für den aktuellen Kernel gebaut wird. Leider bootet dann der Router nicht mehr, ich gehe mal davon aus, dass das Patch nicht mit dem 4.4er Kernel funktioniert.

Aufgrund mangelnder C-Kenntnisse komm ich da auch nicht weiter.

ChristianD

2017-05-26 14:24

manager   ~0000084

B Zeile 109 bis 130. Zumindest das Patch scheint richtig drinnen zu sein. Warum der Router nicht bootet keine Ahnung.
l2tp_eth.c (9,072 bytes)   
/*
 * L2TPv3 ethernet pseudowire driver
 *
 * Copyright (c) 2008,2009,2010 Katalix Systems Ltd
 *
 *	This program is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU General Public License
 *	as published by the Free Software Foundation; either version
 *	2 of the License, or (at your option) any later version.
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/socket.h>
#include <linux/hash.h>
#include <linux/l2tp.h>
#include <linux/in.h>
#include <linux/etherdevice.h>
#include <linux/spinlock.h>
#include <net/sock.h>
#include <net/ip.h>
#include <net/icmp.h>
#include <net/udp.h>
#include <net/inet_common.h>
#include <net/inet_hashtables.h>
#include <net/tcp_states.h>
#include <net/protocol.h>
#include <net/xfrm.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>

#include "l2tp_core.h"

/* Default device name. May be overridden by name specified by user */
#define L2TP_ETH_DEV_NAME	"l2tpeth%d"

/* via netdev_priv() */
struct l2tp_eth {
	struct net_device	*dev;
	struct sock		*tunnel_sock;
	struct l2tp_session	*session;
	struct list_head	list;
	atomic_long_t		tx_bytes;
	atomic_long_t		tx_packets;
	atomic_long_t		tx_dropped;
	atomic_long_t		rx_bytes;
	atomic_long_t		rx_packets;
	atomic_long_t		rx_errors;
};

/* via l2tp_session_priv() */
struct l2tp_eth_sess {
	struct net_device	*dev;
};

/* per-net private data for this module */
static unsigned int l2tp_eth_net_id;
struct l2tp_eth_net {
	struct list_head l2tp_eth_dev_list;
	spinlock_t l2tp_eth_lock;
};

static inline struct l2tp_eth_net *l2tp_eth_pernet(struct net *net)
{
	return net_generic(net, l2tp_eth_net_id);
}

static struct lock_class_key l2tp_eth_tx_busylock;
static int l2tp_eth_dev_init(struct net_device *dev)
{
	struct l2tp_eth *priv = netdev_priv(dev);

	priv->dev = dev;
	eth_hw_addr_random(dev);
	eth_broadcast_addr(dev->broadcast);
	dev->qdisc_tx_busylock = &l2tp_eth_tx_busylock;
	return 0;
}

static void l2tp_eth_dev_uninit(struct net_device *dev)
{
	struct l2tp_eth *priv = netdev_priv(dev);
	struct l2tp_eth_net *pn = l2tp_eth_pernet(dev_net(dev));

	spin_lock(&pn->l2tp_eth_lock);
	list_del_init(&priv->list);
	spin_unlock(&pn->l2tp_eth_lock);
	dev_put(dev);
}

static int l2tp_eth_dev_xmit(struct sk_buff *skb, struct net_device *dev)
{
	struct l2tp_eth *priv = netdev_priv(dev);
	struct l2tp_session *session = priv->session;
	unsigned int len = skb->len;
	int ret = l2tp_xmit_skb(session, skb, session->hdr_len);

	if (likely(ret == NET_XMIT_SUCCESS)) {
		atomic_long_add(len, &priv->tx_bytes);
		atomic_long_inc(&priv->tx_packets);
	} else {
		atomic_long_inc(&priv->tx_dropped);
	}
	return NETDEV_TX_OK;
}

static struct rtnl_link_stats64 *l2tp_eth_get_stats64(struct net_device *dev,
						      struct rtnl_link_stats64 *stats)
{
	struct l2tp_eth *priv = netdev_priv(dev);

	#if BITS_PER_LONG == 64
	stats->tx_bytes   = atomic_long_read(&priv->tx_bytes);
	stats->tx_packets = atomic_long_read(&priv->tx_packets);
	stats->tx_dropped = atomic_long_read(&priv->tx_dropped);
	stats->rx_bytes   = atomic_long_read(&priv->rx_bytes);
	stats->rx_packets = atomic_long_read(&priv->rx_packets);
	stats->rx_errors  = atomic_long_read(&priv->rx_errors);
	#else
	stats->tx_bytes   = (unsigned long) atomic_long_read(&priv->tx_bytes);
	stats->tx_packets = (unsigned long) atomic_long_read(&priv->tx_packets);
	stats->tx_dropped = (unsigned long) atomic_long_read(&priv->tx_dropped);
	stats->rx_bytes   = (unsigned long) atomic_long_read(&priv->rx_bytes);
	stats->rx_packets = (unsigned long) atomic_long_read(&priv->rx_packets);
	stats->rx_errors  = (unsigned long) atomic_long_read(&priv->rx_errors);
	#endif
	return stats;
}


static struct net_device_ops l2tp_eth_netdev_ops = {
	.ndo_init		= l2tp_eth_dev_init,
	.ndo_uninit		= l2tp_eth_dev_uninit,
	.ndo_start_xmit		= l2tp_eth_dev_xmit,
	.ndo_get_stats64	= l2tp_eth_get_stats64,
	.ndo_set_mac_address	= eth_mac_addr,
};

static void l2tp_eth_dev_setup(struct net_device *dev)
{
	ether_setup(dev);
	dev->priv_flags		&= ~IFF_TX_SKB_SHARING;
	dev->features		|= NETIF_F_LLTX;
	dev->netdev_ops		= &l2tp_eth_netdev_ops;
	dev->destructor		= free_netdev;
}

static void l2tp_eth_dev_recv(struct l2tp_session *session, struct sk_buff *skb, int data_len)
{
	struct l2tp_eth_sess *spriv = l2tp_session_priv(session);
	struct net_device *dev = spriv->dev;
	struct l2tp_eth *priv = netdev_priv(dev);

	if (session->debug & L2TP_MSG_DATA) {
		unsigned int length;

		length = min(32u, skb->len);
		if (!pskb_may_pull(skb, length))
			goto error;

		pr_debug("%s: eth recv\n", session->name);
		print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, skb->data, length);
	}

	if (!pskb_may_pull(skb, ETH_HLEN))
		goto error;

	secpath_reset(skb);

	/* checksums verified by L2TP */
	skb->ip_summed = CHECKSUM_NONE;

	skb_dst_drop(skb);
	nf_reset(skb);

	if (dev_forward_skb(dev, skb) == NET_RX_SUCCESS) {
		atomic_long_inc(&priv->rx_packets);
		atomic_long_add(data_len, &priv->rx_bytes);
	} else {
		atomic_long_inc(&priv->rx_errors);
	}
	return;

error:
	atomic_long_inc(&priv->rx_errors);
	kfree_skb(skb);
}

static void l2tp_eth_delete(struct l2tp_session *session)
{
	struct l2tp_eth_sess *spriv;
	struct net_device *dev;

	if (session) {
		spriv = l2tp_session_priv(session);
		dev = spriv->dev;
		if (dev) {
			unregister_netdev(dev);
			spriv->dev = NULL;
			module_put(THIS_MODULE);
		}
	}
}

#if defined(CONFIG_L2TP_DEBUGFS) || defined(CONFIG_L2TP_DEBUGFS_MODULE)
static void l2tp_eth_show(struct seq_file *m, void *arg)
{
	struct l2tp_session *session = arg;
	struct l2tp_eth_sess *spriv = l2tp_session_priv(session);
	struct net_device *dev = spriv->dev;

	seq_printf(m, "   interface %s\n", dev->name);
}
#endif

static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg)
{
	struct net_device *dev;
	char name[IFNAMSIZ];
	struct l2tp_tunnel *tunnel;
	struct l2tp_session *session;
	struct l2tp_eth *priv;
	struct l2tp_eth_sess *spriv;
	int rc;
	struct l2tp_eth_net *pn;

	tunnel = l2tp_tunnel_find(net, tunnel_id);
	if (!tunnel) {
		rc = -ENODEV;
		goto out;
	}

	session = l2tp_session_find(net, tunnel, session_id);
	if (session) {
		rc = -EEXIST;
		goto out;
	}

	if (cfg->ifname) {
		dev = dev_get_by_name(net, cfg->ifname);
		if (dev) {
			dev_put(dev);
			rc = -EEXIST;
			goto out;
		}
		strlcpy(name, cfg->ifname, IFNAMSIZ);
	} else
		strcpy(name, L2TP_ETH_DEV_NAME);

	session = l2tp_session_create(sizeof(*spriv), tunnel, session_id,
				      peer_session_id, cfg);
	if (!session) {
		rc = -ENOMEM;
		goto out;
	}

	dev = alloc_netdev(sizeof(*priv), name, NET_NAME_UNKNOWN,
			   l2tp_eth_dev_setup);
	if (!dev) {
		rc = -ENOMEM;
		goto out_del_session;
	}

	dev_net_set(dev, net);
	if (session->mtu == 0)
		session->mtu = dev->mtu - session->hdr_len;
	dev->mtu = session->mtu;
	dev->needed_headroom += session->hdr_len;

	priv = netdev_priv(dev);
	priv->dev = dev;
	priv->session = session;
	INIT_LIST_HEAD(&priv->list);

	priv->tunnel_sock = tunnel->sock;
	session->recv_skb = l2tp_eth_dev_recv;
	session->session_close = l2tp_eth_delete;
#if defined(CONFIG_L2TP_DEBUGFS) || defined(CONFIG_L2TP_DEBUGFS_MODULE)
	session->show = l2tp_eth_show;
#endif

	spriv = l2tp_session_priv(session);
	spriv->dev = dev;

	rc = register_netdev(dev);
	if (rc < 0)
		goto out_del_dev;

	__module_get(THIS_MODULE);
	/* Must be done after register_netdev() */
	strlcpy(session->ifname, dev->name, IFNAMSIZ);

	dev_hold(dev);
	pn = l2tp_eth_pernet(dev_net(dev));
	spin_lock(&pn->l2tp_eth_lock);
	list_add(&priv->list, &pn->l2tp_eth_dev_list);
	spin_unlock(&pn->l2tp_eth_lock);

	return 0;

out_del_dev:
	free_netdev(dev);
	spriv->dev = NULL;
out_del_session:
	l2tp_session_delete(session);
out:
	return rc;
}

static __net_init int l2tp_eth_init_net(struct net *net)
{
	struct l2tp_eth_net *pn = net_generic(net, l2tp_eth_net_id);

	INIT_LIST_HEAD(&pn->l2tp_eth_dev_list);
	spin_lock_init(&pn->l2tp_eth_lock);

	return 0;
}

static struct pernet_operations l2tp_eth_net_ops = {
	.init = l2tp_eth_init_net,
	.id   = &l2tp_eth_net_id,
	.size = sizeof(struct l2tp_eth_net),
};


static const struct l2tp_nl_cmd_ops l2tp_eth_nl_cmd_ops = {
	.session_create	= l2tp_eth_create,
	.session_delete	= l2tp_session_delete,
};


static int __init l2tp_eth_init(void)
{
	int err = 0;

	err = l2tp_nl_register_ops(L2TP_PWTYPE_ETH, &l2tp_eth_nl_cmd_ops);
	if (err)
		goto out;

	err = register_pernet_device(&l2tp_eth_net_ops);
	if (err)
		goto out_unreg;

	pr_info("L2TP ethernet pseudowire support (L2TPv3)\n");

	return 0;

out_unreg:
	l2tp_nl_unregister_ops(L2TP_PWTYPE_ETH);
out:
	return err;
}

static void __exit l2tp_eth_exit(void)
{
	unregister_pernet_device(&l2tp_eth_net_ops);
	l2tp_nl_unregister_ops(L2TP_PWTYPE_ETH);
}

module_init(l2tp_eth_init);
module_exit(l2tp_eth_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
MODULE_DESCRIPTION("L2TP ethernet pseudowire driver");
MODULE_VERSION("1.0");
MODULE_ALIAS_L2TP_PWTYPE(5);
l2tp_eth.c (9,072 bytes)   

Adrian Schmutzler

2017-05-26 15:06

manager   ~0000085

Hab mir das mal im Code angesehen; was ich nicht verstehe: Die Funktion atomic_long_read gibt immer long zurück. Die Member im struct rtnl_link_stats64 haben aber immer Typ __u64. Es müsste also eigtl. doch auch _immer_ eine Conversion von long nach __u64 (ist ja unsigned) stattfinden, oder?

Adrian Schmutzler

2017-05-26 16:28

manager   ~0000086

Und während u64 immer die gleiche Größe hat, müsste "unsigned long" ja compilerspezifisch sein, also unterschiedliche Größen haben können. Das heißt, gerade wenn z.B. BITS_PER_LONG=32 würde ja dann ggf. eine 32-bit lange unsigned long in eine 64-bit lange __u64 variable gespeichert?! Aber wahrscheinlich müsste man dazu mit dem Ersteller des Patches sprechen, vll. versteh ich da ja auch was falsch.

Adrian Schmutzler

2017-05-26 17:02

manager   ~0000087

Was mir gerade auch noch aufgefallen ist: In der Funktion atomic_long_read kann es unter bestimmten Umständen zu einer Conversion long long nach long kommen, was auch einen Integer Overflow an der selben Stelle auslösen könnte.

Adrian Schmutzler

2017-05-27 18:15

manager   ~0000088

Bei welcher Firmware-Version tritt der Overflow denn definitiv nicht auf? Kann ich mir einfach den letzten Commit der 20170110 ziehen, bauen und dann die entsprechenden Stellen mit der LEDE-Variante vergleichen?

ChristianD

2017-05-27 19:18

manager   ~0000089

Last edited: 2017-05-27 19:19

Alles vor LEDE da war bereits ein Patch mit drinnen.

https://github.com/FreifunkFranken/firmware/commit/fce5b0ceefa9fdddd3f1b690416659785df4e2fc#diff-055d355e728cddb080887618826715a5

Das Patch wurde beim Update auf LEDE rausgenommen, ich hab es dann wieder in LEDE mit eingebaut aber seitdem bootet der Router nicht mehr, warum? Keine Ahnung

mfg

Christian

Adrian Schmutzler

2017-05-28 19:54

manager   ~0000090

Okay, short version:
Bei mir läuft es mit dem alten patch, wenn man nur das Patch-Verzeichnis ändert (3.3.irgendwas nach 4.4, siehe Anhang).

Long version:
1. Ich hab die Firmware mit dem Stand benutzt, die ich für den v12 committed habe (also etwas neuer als die alpha, aber LEDE stable branch). Verwendet zum Testen habe ich einen WR841N v10.
2. Ich hab zur Referenz auch einmal den Overflow mit der standard-alpha reproduziert. Dabei hatte ich beim Upgraden nen Bootloop und musste mit TFTP recovern. Danach lief es wieder, aber vll. ist das dasselbe, was ChristianD passiert ist. (=es war "Zufall")
3. Der Code ist so geschrieben, dass auf 32-bit Geräten auch nur eine 32-bit int als Zwischenvariable verwendet wird. D.h. der Traffic kann keine Werte höher als 4 GB (unsigned int) annehmen, danach läuft er wieder von Null.
4. Ich habe den Code von 20170110 und der alpha näher angesehen, die Routinen sind hier effektiv gleich, sodass der Patch auch den gleichen Effekt haben sollte.
5. Vor dem Test hab ich jeweils fastd über SSH deaktiviert und dann per Download genügend Traffic produziert (mir ist keine einfachere Methode eingefallen)

... Mehr fällt mir grade nicht ein
0004-ar71xx-3.18-l2tp-stats.patch (1,729 bytes)   
--- target/linux/ar71xx/patches-4.4/fix-l2tp-stats-couter-on-32-Bit-Systems.patch	1970-01-01 01:00:00.000000000 +0100
+++ target/linux/ar71xx/patches-4.4/fix-l2tp-stats-couter-on-32-Bit-Systems.patch	2016-03-12 18:41:32.818602442 +0100
@@ -0,0 +1,38 @@
+From 19b1c8733b99f13005f2d8918bce588f0b2556f8 Mon Sep 17 00:00:00 2001
+From: Dominik Heidler <dominik@heidler.eu>
+Date: Sat, 12 Mar 2016 18:37:42 +0100
+Subject: [PATCH] Fix l2tp stats couter on 32 Bit Systems
+
+---
+ net/l2tp/l2tp_eth.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c
+index e253c26..a18c2ff 100644
+--- a/net/l2tp/l2tp_eth.c
++++ b/net/l2tp/l2tp_eth.c
+@@ -111,12 +111,21 @@ static struct rtnl_link_stats64 *l2tp_eth_get_stats64(struct net_device *dev,
+ {
+ 	struct l2tp_eth *priv = netdev_priv(dev);
+ 
++	#if BITS_PER_LONG == 64
+ 	stats->tx_bytes   = atomic_long_read(&priv->tx_bytes);
+ 	stats->tx_packets = atomic_long_read(&priv->tx_packets);
+ 	stats->tx_dropped = atomic_long_read(&priv->tx_dropped);
+ 	stats->rx_bytes   = atomic_long_read(&priv->rx_bytes);
+ 	stats->rx_packets = atomic_long_read(&priv->rx_packets);
+ 	stats->rx_errors  = atomic_long_read(&priv->rx_errors);
++	#else
++	stats->tx_bytes   = (unsigned long) atomic_long_read(&priv->tx_bytes);
++	stats->tx_packets = (unsigned long) atomic_long_read(&priv->tx_packets);
++	stats->tx_dropped = (unsigned long) atomic_long_read(&priv->tx_dropped);
++	stats->rx_bytes   = (unsigned long) atomic_long_read(&priv->rx_bytes);
++	stats->rx_packets = (unsigned long) atomic_long_read(&priv->rx_packets);
++	stats->rx_errors  = (unsigned long) atomic_long_read(&priv->rx_errors);
++	#endif
+ 	return stats;
+ }
+ 
+-- 
+2.7.2
+

ChristianD

2017-05-28 21:05

manager   ~0000091

Last edited: 2017-05-28 21:06

ich hab es auf einen wdr3600 geflasht. Leider keine UART dran zum gucken was passiert. Aber er bootet ein paar Sekunden, dann leuchten alle LEDs auf, dann wieder nur Power und nach etwa. 10-15Sekunden wieder alle LEDs dann nur Power usw... in einer Endlosschleife. Sieht mir nach ner bootloop aus.

Ich hab bei mir auch nur das Kernelverzeichnis geändert (wie du sagtest von 3.xirgendwas auf 4.4irgendwas) damit das Problem entstand

tftp&kram hab ich noch nicht probiert.

Adrian Schmutzler

2017-05-28 21:11

manager   ~0000092

Genau das Verhalten hatte ich auch, als ich nur die offizielle alpha geflasht habe (was aber vorher schon mal funktioniert hat). Danach ging es auch wieder (20170110 factory per TFTP, dann Upgrade auf alpha), k.A. was hier der Grund war. Entsprechend würde ich es an deiner Stelle auf einen zweiten Versuch ankommen lassen.

Adrian Schmutzler

2017-05-29 15:21

manager   ~0000094

Hab das ganze jetzt mal als Patch geschickt, der nach meinen drei anderen applied werden kann. Hab gestern auch noch einen Router (WR841N v11) im "Produktiv-Einsatz" geflasht (WebUI per Remote), der seitdem auch problemlos läuft:
https://monitoring.freifunk-franken.de/routers/58d662e49369c34df4bd7081

Adrian Schmutzler

2017-06-03 00:01

manager   ~0000100

Status -> fixed.

Issue History

Date Modified Username Field Change
2017-03-12 15:57 ChristianD New Issue
2017-05-26 14:15 ChristianD Note Added: 0000083
2017-05-26 14:24 ChristianD File Added: l2tp_eth.c
2017-05-26 14:24 ChristianD Note Added: 0000084
2017-05-26 15:06 Adrian Schmutzler Note Added: 0000085
2017-05-26 16:28 Adrian Schmutzler Note Added: 0000086
2017-05-26 17:02 Adrian Schmutzler Note Added: 0000087
2017-05-27 18:15 Adrian Schmutzler Note Added: 0000088
2017-05-27 19:18 ChristianD Note Added: 0000089
2017-05-27 19:19 ChristianD Note Edited: 0000089
2017-05-28 19:54 Adrian Schmutzler File Added: 0004-ar71xx-3.18-l2tp-stats.patch
2017-05-28 19:54 Adrian Schmutzler Note Added: 0000090
2017-05-28 21:05 ChristianD Note Added: 0000091
2017-05-28 21:06 ChristianD Note Edited: 0000091
2017-05-28 21:11 Adrian Schmutzler Note Added: 0000092
2017-05-29 15:21 Adrian Schmutzler Note Added: 0000094
2017-06-03 00:01 Adrian Schmutzler Note Added: 0000100
2017-06-04 08:36 ChristianD Status new => resolved
2017-06-04 08:36 ChristianD Resolution open => fixed
2017-06-04 08:37 ChristianD Status resolved => closed
2019-10-02 12:48 fbl Category Freifunk Franken Firmware => General
2019-10-02 12:48 fbl Category General => General2
2019-10-02 12:49 fbl Category General2 => General